수집품 포털 생성의 마지막 단계에 오신 것을 환영합니다! (1부 내용은 여기를 참조하세요)
이 부분에서는 퍼즐의 마지막 조각인 프런트엔드 구축에 중점을 둘 것입니다.
우리가 달성할 내용은 다음과 같습니다.
우리는 프론트엔드를 구축하기 위해 Next.js를 사용할 것입니다.
시작하자!
프로젝트 flow-collectible-portal
디렉터리를 엽니다. 그런 다음 실행
터미널에서 npx create-next-app@latest frontend
입력하고 enter
누르세요.
그러면 몇 가지 옵션이 제공됩니다. 이 튜토리얼에서는 Typescript , ESLint 또는 TailwindCSS를 사용하지 않을 것이며, 이 글을 작성할 당시에는 src
디렉토리와 앱 라우터를 사용할 것입니다.
이제 새로운 웹 앱이 준비되었습니다.
프런트엔드 폴더는 다음과 같습니다.
Flow 블록체인 과 상호 작용하기 위해 FCL(Flow 클라이언트 라이브러리)을 사용하여 지갑 연결을 관리하고, 스크립트를 실행하고, 애플리케이션에서 트랜잭션을 보냅니다. 이를 통해 완전한 Cadence 함수를 작성하고 이를 Javascript 함수로 실행할 수 있습니다.
시작하려면 다음 명령을 실행하여 앱용 FCL을 설치해 보겠습니다.
npm install @onflow/fcl --save
FCL을 설치한 후에는 이를 구성해야 합니다. 수행해야 할 작업은 다음과 같습니다.
app
폴더 안에 flow
라는 새 폴더를 만들고 config.js
라는 파일을 추가합니다.
config.js
파일에 다음 코드를 추가합니다.
import { config } from "@onflow/fcl"; config({ "app.detail.title": "Flow Name Service", "app.detail.icon": "https://placekitten.com/g/200/200", "accessNode.api": "https://rest-testnet.onflow.org", "discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn", "0xCollectibles": "ADD YOUR CONTRACT ACCOUNT ADDRESS", "0xNonFungibleToken": "0x631e88ae7f1d7c20", });
이제 앱에서 FCL을 사용하도록 모든 설정이 완료되었습니다.
앱에서 사용자의 신원을 확인하려면 다음과 같은 여러 기능을 사용할 수 있습니다.
fcl.logIn()
호출하세요.fcl.signUp()
호출하세요.fcl.unauthenticate()
호출하세요.
프런트엔드에서 이러한 fcl
기능을 구현하는 방법을 알아봅시다.
먼저 앱 디렉터리 내의 page.js
파일에 다음 코드를 추가합니다. 그러면 일부 종속성을 가져오고, 앱 일부에 대한 초기 useState
설정하고, 기본 UI가 구축됩니다.
멋지게 보이도록 하려면 앱 디렉터리에서 page.module.css
파일을 삭제하고 대신 page.css라는 파일을 만드세요. 그런 다음 이 파일 의 내용을 그 안에 붙여넣습니다. 이제 초기 페이지를 작성할 수 있습니다.
"use client"; import React, { useState, useEffect, useRef } from "react"; import * as fcl from "@onflow/fcl"; import "./page.css"; import "./flow/config"; export default function Page() { const [currentUser, setCurrentUser] = useState({ loggedIn: false, addr: undefined, }); const urlInputRef = useRef(); const nameInputRef = useRef(); const idInputRef = useRef(); const [isInitialized, setIsInitialized] = useState(); const [collectiblesList, setCollectiblesList] = useState([]); const [loading, setLoading] = useState(false); const [ids, setIds] = useState([]); const [nft, setNFT] = useState({}); useEffect(() => fcl.currentUser.subscribe(setCurrentUser), []); function handleInputChange(event) { const inputValue = event.target.value; if (/^\d+$/.test(inputValue)) { idInputRef.current = +inputValue; } else { console.error("Invalid input. Please enter a valid integer."); } } return ( <div> <div className="navbar"> <h1>Flow Collectibles Portal</h1> <span>Address: {currentUser?.addr ?? "NO Address"}</span> <button onClick={currentUser.addr ? fcl.unauthenticate : fcl.logIn}> {currentUser.addr ? "Log Out" : "Connect Wallet"} </button> </div> {currentUser.loggedIn ? ( <div className="main"> <div className="mutate"> <h1>Mutate Flow Blockchain</h1> <form onSubmit={(event) => { event.preventDefault(); }} > <input type="text" placeholder="enter name of the NFT" ref={nameInputRef} /> <input type="text" placeholder="enter a url" ref={urlInputRef} /> <button type="submit">Mint</button> </form> <mark>Your Collection will be initialized while minting NFT.</mark> </div> <div className="query"> <h1>Query Flow Blockchain</h1> <mark>Click below button to check 👇</mark> <button>Check Collection</button> <p> Is your collection initialized: {isInitialized ? "Yes" : "No"} </p> <button onClick={viewIds}> View NFT IDs you hold in your collection </button> <p>NFT Id: </p> </div> <div className="view"> <h1>View Your NFT</h1> <input type="text" placeholder="enter your NFT ID" onChange={handleInputChange} /> <button>View NFT</button> <div className="nft-card"> <p>NFT id: </p> <p>NFT name: </p> <img src="" alt="" /> </div> </div> </div> ) : ( <div className="main-2"> <h1>Connect Wallet to mint NFT!!</h1> </div> )} </div> ); }
이 코드를 추가한 후 npm run dev
실행하여 모든 것이 올바르게 로드되는지 확인하세요.
fcl
사용하여 Flow 블록체인을 쿼리하는 방법을 자세히 알아보기 전에 page.js
파일의 handleInput
함수 뒤에 이러한 Cadence 스크립트 코드를 추가하세요.
const CHECK_COLLECTION = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles pub fun main(address: Address): Bool? { return Collectibles.checkCollection(_addr: address) }` const GET_NFT_ID = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles pub fun main(user: Address): [UInt64] { let collectionCap = getAccount(user).capabilities.get <&{Collectibles.CollectionPublic}>(/public/NFTCollection) ?? panic("This public capability does not exist.") let collectionRef = collectionCap.borrow()! return collectionRef.getIDs() } ` const GET_NFT = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles pub fun main(user: Address, id: UInt64): &NonFungibleToken.NFT? { let collectionCap= getAccount(user).capabilities.get<&{Collectibles.CollectionPublic}>(/public/NFTCollection) ?? panic("This public capability does not exist.") let collectionRef = collectionCap.borrow()! return collectionRef.borrowNFT(id: id) }
Cadence 스크립트가 준비되었으므로 이제 일부 Javascript 함수를 선언하고 Cadence 상수를 `fcl`
쿼리에 전달할 수 있습니다.
async function checkCollectionInit() { const isInit = await fcl.query({ cadence: CHECK_COLLECTION, args: (arg,t) => [arg(currentUser?.addr, t.Address)], }); console.log(isInit); } async function viewNFT() { console.log(idInputRef.current); const nfts = await fcl.query({ cadence: GET_NFT, args: (arg,t) => [arg(currentUser?.addr,t.Address), arg(idInputRef.current, t.UInt64)] }); setNFT(nfts); console.log(nfts); } async function viewIds() { const ids = await fcl.query({ cadence: GET_NFT_ID, args: (arg,t) => [arg(currentUser?.addr,t.Address)] }); setIds(ids); console.log(ids); }
이제 우리가 작성한 모든 함수를 살펴보겠습니다. 주의할 점은 두 가지입니다.
fcl.query
args: (arg,t) => [arg(addr,t.Address)],
line.
스크립트는 Solidity의 view
기능과 유사하고 실행하는 데 가스 요금이 필요하지 않으므로 기본적으로 블록체인에 쿼리만 하면 됩니다. 그래서 우리는 fcl.query
사용하여 Flow에서 스크립트를 실행합니다.
무언가를 쿼리하려면 인수를 전달해야 합니다. 이를 위해 인수를 나타내는 문자열 값을 취하는 함수인 arg와 Cadence가 가지고 있는 다양한 데이터 유형을 모두 포함하는 객체인 t
사용합니다. 따라서 우리는 전달하는 인수를 인코딩하고 디코딩하는 방법을 arg
알릴 수 있습니다.
이전 기능은 "읽기 전용"에 불과했지만 다음 기능에는 블록체인 상태를 변경하고 기록할 수 있는 작업이 포함됩니다. 일명 "NFT 발행".
이를 위해 또 다른 Cadence 스크립트를 상수로 작성하겠습니다.
const MINT_NFT = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles transaction(name:String, image:String){ let receiverCollectionRef: &{NonFungibleToken.CollectionPublic} prepare(signer:AuthAccount){ // initialise account if signer.borrow<&Collectibles.Collection>(from: Collectibles.CollectionStoragePath) == nil { let collection <- Collectibles.createEmptyCollection() signer.save(<-collection, to: Collectibles.CollectionStoragePath) let cap = signer.capabilities.storage.issue<&{Collectibles.CollectionPublic}>(Collectibles.CollectionStoragePath) signer.capabilities.publish( cap, at: Collectibles.CollectionPublicPath) } //takes the receiver collection refrence self.receiverCollectionRef = signer.borrow<&Collectibles.Collection>(from: Collectibles.CollectionStoragePath) ?? panic("could not borrow Collection reference") } execute{ let nft <- Collectibles.mintNFT(name:name, image:image) self.receiverCollectionRef.deposit(token: <-nft) } }
이제 page.js
파일의 트랜잭션 코드 뒤에 아래 함수를 추가합니다.
async function mint() { try{ const txnId = await fcl.mutate({ cadence: MINT_NFT, args: (arg,t) => [arg(name,t.String), arg(image, t.String)], payer: fcl.authz, proposer: fcl.authz, authorizations: [fcl.authz], limit:999,}); } catch(error){ console.error('Minting failed:' error) } console.log(txnId); }
함수의 경우 fcl.mutate
구문은 fcl.query
와 동일합니다. 그러나 다음과 같은 몇 가지 추가 매개변수를 제공합니다.
payer: fcl.authz, proposer: fcl.authz, authorizations: [fcl.authz], limit: 50,
fcl.authz
현재 연결된 계정을 나타냅니다.limit
최대 계산량에 상한선을 설정하는 이더리움 세계의 gasLimit과 같습니다. 계산이 한도를 초과하면 트랜잭션이 실패합니다.
방금 만든 mintNFT
함수를 호출하고 처리하는 함수를 하나 더 추가해야 합니다.
const saveCollectible = async () => { if (urlInputRef.current.value.length > 0 && nameInputRef.current.value.length > 0) { try { setLoading(true); const transaction = await mintNFT(nameInputRef.current.value, urlInputRef.current.value); console.log('transactionID:', transaction); // Handle minting success (if needed) } catch (error) { console.error('Minting failed:', error); // Handle minting failure (if needed) } finally { setLoading(false); } } else { console.log('Empty input. Try again.'); } };
주요 기능이 준비되었으므로 이제 이를 UI에 연결할 수 있습니다.
하지만 그 전에 초기 상태를 로드하는 데 도움이 되는 몇 가지 useEffect
호출을 추가하겠습니다. 이미 존재하는 useEffect
호출 바로 위에 이를 추가할 수 있습니다.
useEffect(() => { checkCollectionInit(); viewNFT(); }, [currentUser]); useEffect(() => { if (currentUser.loggedIn) { setCollectiblesList(collectiblesList); console.log('Setting collectibles...'); } }, [currentUser]);
이제 UI가 있는 return
섹션으로 돌아가 앱의 적절한 부분에 기능을 추가할 수 있습니다.
return ( <div> <div className="navbar"> <h1>Flow Collectibles Portal</h1> <span>Address: {currentUser?.addr ?? "NO Address"}</span> <button onClick={currentUser.addr ? fcl.unauthenticate : fcl.logIn}> {currentUser.addr ? "Log Out" : "Connect Wallet"} </button> </div> {currentUser.loggedIn ? ( <div className="main"> <div className="mutate"> <h1>Mutate Flow Blockchain</h1> <form onSubmit={(event) => { event.preventDefault(); saveCollectible(); }} > <input type="text" placeholder="enter name of the NFT" ref={nameInputRef} /> <input type="text" placeholder="enter a url" ref={urlInputRef} /> <button type="submit">Mint</button> </form> <mark>Your Collection will be initialized while minting NFT.</mark> </div> <div className="query"> <h1>Query Flow Blockchain</h1> <mark>Click below button to check 👇</mark> <button onClick={checkCollectionInit}>Check Collection</button> <p> Is your collection initialized: {isInitialized ? "Yes" : "No"} </p> <button onClick={viewIds}> View NFT IDs you hold in your collection </button> <p>NFT Id: </p> {ids.map((id) => ( <p key={id}>{id}</p> ))} </div> <div className="view"> <h1>View Your NFT</h1> <input type="text" placeholder="enter your NFT ID" onChange={handleInputChange} /> <button onClick={viewNFT}>View NFT</button> <div className="nft-card"> <p>NFT id: {nft.id}</p> <p>NFT name: {nft.name}</p> <img src={nft.image} alt={nft.name} /> </div> </div> </div> ) : ( <div className="main-2"> <h1>Connect Wallet to mint NFT!!</h1> </div> )} </div> );
여기에서 최종 코드를 확인하세요.
이제 앱이 완성되었으므로 사용 방법을 살펴보겠습니다.
먼저 오른쪽 상단의 '지갑 연결' 버튼을 클릭하여 지갑을 연결하세요.
이제 NFT를 발행할 수 있습니다! NFT 이름을 입력하고 사용하려는 이미지의 링크를 붙여넣으세요. "민트"를 클릭하면 지갑으로 거래에 서명하라는 메시지가 표시됩니다.
거래가 완료되는 데 약간의 시간이 걸릴 수 있습니다. 완료되면 하단 버튼을 클릭하여 NFT의 ID를 볼 수 있습니다. 이것이 첫 번째인 경우 ID는 "1"이어야 합니다.
이제 NFT의 ID를 복사하여 보기 섹션에 붙여넣고 “NFT 보기”를 클릭할 수 있습니다.
잘하셨어요! 수집품 포털 프로젝트의 2부를 완료했습니다. 요약하자면, 우리는 수집품 포털의 프런트엔드를 구축하는 데 중점을 두었습니다.
우리는 다음과 같이 이를 수행했습니다.
정말 좋은 하루 보내세요!
여기에도 게시되었습니다 .