OpenSea 클론코딩 해보기

CY·2022년 2월 18일
0

첫 프로젝트 시작

정신을 차려보니 프로젝트 시작..?

블록체인 부트캠프에 막 들어와 정신없이 Section 1을 공부하던 때가 며칠전 같은데
벌써 15주라는 시간이 지나 드디어 첫 팀 프로젝트를 시작하게 되었다.

물론 그동안 Pair Programming을 통해 2~3명씩 팀원들과 배운것들을 익히기 위해 간단한 프로젝트들은 해봤지만, 이번엔 그동안 배운 모든 것들을 활용해본다고 생각하니 새삼 가슴이 두근거리고 내가 잘 해낼 수 있을까라는 괜한 걱정도 들었다.

뱁새가 황새를 따라가면 다리 찢어진다

이번에 주어진 주제는 NFT 마켓 플랫폼으로 가장 유명한 OpenSea를 따라 클론 코딩해보는 것이다. 하단의 사진이 우리가 목표로 하는 OpenSea의 기본 모습이다.

물론 여기 보여지는 Explore 외에도 현재 핫한 NFT 랭킹이라던가, 고객센터, NFT 거래, 내 정보 등 다양한 페이지가 있지만 주어진 일주일이란 시간에 너무 많은걸 목표로 하면 이도저도 아니게 될 것 같아 프로젝트를 시작하기 전 팀원들과 최소한의 목표치를 정해놓고 살을 붙여가기로 하였다.

팀원들과 회의를 거쳐 나온 목표들

  • 거래소에서 사용 될 NFT를 RopSten 테스트넷 상에 발행하기
  • 발행된 NFT들을 거래소에서 보여주고 전송
  • NFT를 거래할 ERC20 토큰 발행 및 전송

위의 목표들을 3명의 팀원이 하나씩 맡아 기능 구현을 해보기로 하였고
나는 2번째 목표인 NFT를 보여주고 주고 받는 것을 맡게 되었다.

기능 구현

사용 된 스택 및 언어

  • JavaScript, CSS, Solidity, React, web3

Explore 페이지


각 NFT의 카테고리들은 거래소에 등록되어 있다고 가정하여 기존의 정보를 바로 페이지에 뿌려주었으며
거래를 원하는 NFT를 클릭하였을 때 해당 NFT의 정보를 Contract에서 받아와 보여주는 식으로 구현하였다.

NFT 전송


처음 전환되는 페이지는 내 소유의 NFT를 보여주는 곳이다.
페이지에 들어오면 Contract에서 해당 Account가 소유한 NFT 목록을 읽어와 보여준다.

여기에서 Test1계정이 6개의 NFT를 소유하고 있으며 이 중 2개의 NFT를 Test2에게 전송하는 장면이다.


이어서 metamask의 알림을 통해 transaction이 성공적으로 수행된 것을 확인한 뒤
Test2 계정으로 전환해보면 2개의 NFT가 성공적으로 전송된 것을 확인할 수 있다.

이번 프로젝트를 뒤돌아보며

프로젝트를 통해 배운점

내가 그동안 배웠던 개념들을 실제 프로젝트에 적용시켜보며 좀 더 기술적인 지식을 확고히 할 수 있는 기회가 되어 좋았다. 또한 내가 모든 개발을 하던 작은 프로젝트들과는 달리 팀 프로젝트에서 부분별로 나눠서 의사소통을 통해 맞춰나가는 것도 새롭고 좋은 경험이었다.

Problem

처음 프로젝트를 시작할 때 팀원들과 회의하다보니 서버의 개발과 유지보수 측면에 신경쓰는거보다 Contract 상에 데이터를 넣어 서버를 대체하게 하는 것은 어떨까라는 의견이 나와 이를 토대로 프로젝트를 시작하였다.
그런데 하다보니 거래되는 가격이라던가 Contract에 기입된 고정된 data만으로는 부족하다는 생각이 많이 들었다. 간단한 API라도 만들어 Node.js 서버라던가 MySQL을 활용해 필요한 데이터들을 따로 관리하는 것이 더 좋았을 것 같다.

Try

만들다보니 생각보다 React 코드에 State가 많아져 Redux등을 활용해 처음부터 체계적으로 상태 관리를 했으면 어땠을까라는 생각이 들었다.

Code & Git Repository

GitHub
OpenSea Clone Coding - 팀 tim-berners-lee

Contract Solidity Code
(Solidity Code는 GitHub에 추가되지 않아 따로 첨부)

  • 예시) Fidenza.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";

contract Fidenza is ERC721URIStorage, Ownable, ERC721Enumerable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    mapping(uint256 => uint256) price;

    constructor() public ERC721("Fidenza by Tyler Hobbs", "Fidenza") {}

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId)
        internal
        override(ERC721, ERC721URIStorage)
    {
        super._burn(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function mintNFT(
        address recipient,
        string memory tokenURI,
        uint256 _price
    ) public onlyOwner returns (uint256) {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();

        price[newItemId] = _price;
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }

    function tokenPrice(uint256 tokenId) public view returns (uint256) {
        return price[tokenId];
    }
}
profile
CY's StudyRoom

0개의 댓글