[프로젝트] NFT애플리케이션 만들기(OpenSea)

조미진·2022년 2월 18일
0

블록체인 실습

목록 보기
4/5
post-thumbnail

📚 INTRO

오픈씨(OpenSea)는 현재 가장 유명한 NFT마켓플레이스죠!
오픈씨에서는 NFT 거래뿐만 아니라 내 지갑을 연결해서 내 소유의 NFT를 보여주기도하고,
NFT를 생성할 수도 있습니다.

오늘은 이런 오픈씨의 기능을 참고하여 NFT애플리케이션을 만들어보겠습니다🤗

구현할 기능👇

  1. 내 지갑을 연결해서 내 소유의 NFT를 보여준다
  2. 새로운 NFT를 생성한다
  3. NFT를 구매한다

🔎 SR (Software Requirements)

🧑‍💻 기술 스택

  • Node.js / Express
  • React / Next.js
  • Solidity
  • Github

🧑‍💻 주요기능

  • 지갑연결
  • 내 소유의 NFT 확인
  • 새로운 NFT 생성

🧑‍💻 와이어프레임

🧑‍💻 FlowChart

🧑‍💻 DB스키마

🔎 IPFS

IPFS는 분산된 P2P파일 시스템에 안전하게 NFT를 저장할 수 있기 때문에,
일반적인 URL과 같은 링크 끊김, 404오류와 같은 문제를 해결할 수 있습니다.
IPFS사용법은 따로 포스팅하겠습니다.

📷 설치하기

GUI
👉 Mac Desktop 설치
👉 Ubuntu Desktop 설치
CLI
👉 Mac Command-line 설치
👉 Linux Command-line 설치

🔎 메타마스크 지갑연결

지갑 소프트웨어는 웹 페이지에 자바스크립트 객체 형태로 window.ethertum객체를 제공합니다.
window.ethertum은 공급자 객체이기 때문에 web3.js를 사용할 수 있습니다.

useEffect()를 사용해 컴포넌트가 처음 마운트 되었을 때 web3객체를 연결합니다.

function App() {
		const [web3, setWeb3] = useState();
    useEffect(() => {
        if (typeof window.ethereum !== "undefined") { // window.ethereum이 있다면
            try {
                const web = new Web3(window.ethereum);  // 새로운 web3 객체생성
                setWeb3(web);
            } catch (err) {
                console.log(err);
            }
        }
    }, []);
}

메타마스크와 연결하는 버튼을 하나 만들고 버튼을 누르면 함수가 실행되게 합니다!

function App() {
	const [account, setAccount] = useState('');
	// ...

	const connectWallet = async () => {
        accounts = await window.ethereum.request({
            method: "eth_requestAccounts",
        });

        setAccount(accounts[0]);
    };

	return (
        <div className="App">
            <button
                className="metaConnect"
                onClick={() => {
                    connectWallet();
                }}
            >
                connect to MetaMask
            </button>
            <div className="userInfo">주소: {account}</div>
        </div>
    );
}

다음과 같이 지갑이 연결되는 모습을 볼 수 있습니다!

🔎 Mint NFT

Remix에서 NFT를 민팅하는 코드를 작성합니다.

//생략
    function mintNFT(address recipient, string memory tokenURI) public onlyOwner returns (uint256) {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}

Ropsten 네트워크에 컨트랙트를 배포하고, mintNFT함수로 NFT를 민팅합니다.

mintNFT 부분의 인자를 추가하고싶었지만, 생각보다 너무 어려워서 실패.. 아직 너무너무 아쉽습니다,,,😭

🔎 NFT받기

컨트랙트 주소를 입력받아 발행받은 토큰을 불러올 수 있습니다.

function App() {
	const [newErc721addr, setNewErc721Addr] = useState();

	// 생략

	const addNewErc721Token = () => {}

	return (
	// 생략
	<div className="newErc721">
	    <input
	        type="text"
	        onChange={(e) => {
	            setNewErc721addr(e.target.value);  // 입력받을 때마다 newErc721addr 갱신
	        }}
	    ></input>
	    <button onClick={addNewErc721Token}>add new erc721</button>
	</div>
	)
}

해당 컨트랙트 주소를 입력하고 버튼을 누르면 컨트랙트 객체를 생성하고 함수를 호출하여
계정이 가지는 토큰의 목록을 저장합니다.

우리는 컨트랙트의 이름, 심볼, 총 발행량을 가져옵니다.

function App () {
	const [erc721list, setErc721list] = useState([]);  // 자신의 NFT 정보를 저장할 토큰

	const addNewErc721Token = async () => {
		// 생략
			let arr = [];
		  for (let i = 1; i <= totalSupply; i++) {
		      arr.push(i);
		  }
		  
		  for (let tokenId of arr) {
		      let tokenOwner = await tokenContract.methods
		          .ownerOf(tokenId)
		          .call();
		      if (String(tokenOwner).toLowerCase() === account) {
		          let tokenURI = await tokenContract.methods
		              .tokenURI(tokenId)
		              .call();
		          setErc721list((prevState) => {
		              return [...prevState, { name, symbol, tokenId, tokenURI }];
		          });
		      }
		  }
	}
}

🔎 NFT 전송하기

ERC721컨트랙트의 transferFrom함수를 통해 토큰을 이동시킵니다.

App.js로부터 web3과 account를 가져옵니다.

// App.js
function App () {
	return (
	//...
	<TokenList web3={web3} account={account} erc721list={erc721list} />
	)
}
// TokenList.js
function TokenList({ web3, account, erc721list }) {
    return (
        <div className="tokenlist">
            <Erc721 web3={web3} account={account} erc721list={erc721list} />
        </div>
    );
}

다음으로 input박스와 버튼을 추가합니다.

📚 개발 회고

이번 프로젝트는 나에게 너무 힘든 시간이었다😭
그래도 이번 프로젝트를 되새겨보며 정리하는 시간을 가져봐야겠다!

Keep
이번 프로젝트에서 내가 잘한점이 있을까..?
정말 담당한 부분을 해내기 위해서 최선을 다했다고 자신있게 말할수는 있지만 결국 완성해낸것은 없었다😭
하지만 삽질하는 과정에서 얻은 지식이 정말 많고 앞으로도 더 열심히 삽질해보려고 한다!

Problem
하고싶은것과 할수있는것을 구분하자!
프론트 부분은 해본적이 있어서 이번에 서버를 너무 해보고싶었다.
팀원들에게 양해를 구하고 서버를 해보려했지만 아직 무리인것같다!
해보고 안될때는 포기할줄도 알아야하는데 될것같은데? 하면서 너무오랜시간을 써버렸다.
결국 해결하지 못해서 마음이 불편했고 결과도 좋지 않았기에
안된다면 과감히 포기하는것도 방법이라는 것을 알게되었다.

TRY
이번 프로젝트를 진행하면서 내 실력이 부족해서 해결하지 못한 문제가 너무 많다.
블로깅을 마치고 서버부분을 더 공부해서 해결하지 못한 부분을 꼭 해결해내고 싶다.

팀 크루즈라는 이름으로 호기롭게 시작했지만 무능한 선장이 키를 잡아버리는 바람에
타이타닉이 되어버린 우리팀...😭
이번 프로젝트를 마무리하며 약간의 독기와오기가 생겨버렸다.
일단 믿고 따라준 팀원들을 두고 제대로 못해준것도 너무 속상했고, 무능한 나에게 화가 많이 나서
정말 힘든 시간이었다. 하지만 조금더 공부하면 극복해낼 수 있는 시간이었기에
앞으로 더 열심히 공부해야할 원동력이 된 것 같다.얍🔥

profile
Hello World! 🙌

0개의 댓글