클론코딩 결과물 코드(Github)
https://bit.ly/3LCe5zP
OpenSea란?
OpenSea는 Cypto 소장품, NFT(Non Fungible Token) 게임 아이템과 기타 이더리움 블록체인기반의 아이템을 포함한 디지탈 자산의 거래를 지원하는 P2P방식의 마켓이다.
(출처: 해시넷)
이번 팀 프로젝트에서는 OpenSea 사이트를 클론코딩 하였다. Frontend는 React를 사용하였고, Backend에서는 node.js와 web3.js를 사용하였다. 스마트 컨트랙트는 Solidity를 사용하였다. 여기서 나는 Frontend을 맡았다.
페이지는 아래와 같이 구성하였다.
Home: 먼저 처음 접속시 볼 수 있는 화면
Explore: 거래 할 수 있는 NFT 목록이 출력되는 화면
Create: NFT를 생성하고 등록하는 화면
ConnectWallet: 클릭 시 MetaMask에 연결
코드 구성은 아래와 같다.
├── App.js ├── components │ ├── Erc721.css │ ├── Erc721.js │ ├── Navbar.css │ ├── Navbar.js │ ├── Search.css │ ├── Search.js │ ├── TokenList.css │ └── TokenList.js ├── index.css ├── index.js ├── pages │ ├── Create.js │ ├── Detail.js │ ├── Explore.js └─ └── Home.js
Navbar.js
// Navbar.js
import React, { useState} from 'react';
import './Navbar.css';
import Search from './Search';
import { Link } from 'react-router-dom';
const Navbar = () => {
// 지갑 연결
// 원래는 const로 해야되는데 warning 뜨고 페이지가 멈춰버려서 임시로 let을 씀
let [account, setAccount] = useState('');
const connectWallet = async () => {
account = await window.ethereum.request({
method: "eth_requestAccounts",
});
setAccount(account[0]);
};
return (
<div className="navbar">
<Search />
<ul className="link">
<Link to="/" style={{ textDecoration: 'none', color: 'inherit' }}><li>Home</li></Link>
<Link to="/explore" style={{ textDecoration: 'none', color: 'inherit' }}><li>Explore</li></Link>
<Link to="/create" style={{ textDecoration: 'none', color: 'inherit' }}><li>Create</li></Link>
<li calssName='metaConnect' style={{cursor: 'pointer'}}onClick={() => {
connectWallet(); console.log(account);
}}>ConnectWallet</li>
</ul>
</div>
);
};
export default Navbar;
사이트내 어떤 페이지에서도 NFT를 검색하거나 다른 페이지로 이동하기 쉽게 네비게이션 바(Navigation Bar)를 구현하였다. 깔끔한 디자인을 위해 Bootstrap을 활용하였다.
여기서 useState
를 const
로 선언해야하지만, const
사용시 페이지가 멈추는 현상이 발생하여 let
으로 선언하였다.
페이지 이동은 react-router-dom
의 Link
를 사용하여 각각의 텍스트를 클릭시 할당된 페이지로 이동하게 구현하였다.
ConnetWallet
는 비동기 방식으로 구현되었으며 클릭 시 MetaMask와 연결 여부를 묻는 창이 뜬다.
그 외 Link
를 사용하면 텍스트 위에 마우스를 올릴시 클릭 할 수 있는지 구분 할 수 있게 마우스 포인터가 바뀐다.
나는 ConnectWallet
을 Link
로 구현하기 어려워서 CSS Style
속성인 point: cursor
를 이용하여 클릭 할 수 있는지 사용자 입장에서 구분 할 수 있게 구현하였다.
하위 컴포넌트로는 Search.js
를 불러온다.
Search.js
// Search.js
import React from 'react';
import './Search.css';
const Search = () => {
return (
<div class="container">
<div class="row">
<div class="col-md-6">
<div id="custom-search-input">
<div class="input-group col-md-12">
<input type="text" class="form-control input-lg" placeholder="Search..." />
<span class="input-group-btn">
<button class="btn btn-info btn-lg" type="button">
<i class="glyphicon glyphicon-search"></i>
</button>
</span>
</div>
</div>
</div>
</div>
</div>
);
}
export default Search;
Navbar.js
에서 불러오는 하위 컴포넌트로 이것 또한 Bootstrap를 활용하였다.
Home.js
// Home.js
import React from 'react';
import { Link } from 'react-router-dom';
const Home = () => {
const HeadText = "Discover, collect, and sell extraordinary NFTs"
const DescText = "OpenSea is the world's first and largest NFT marketplace"
return(
<div>
<div style={{float: 'left', width: '40%', padding: '3% 3% 3% 3%', margin: 'auto'}}>
<div style={{margin: "auto"}}>
<h1 style={{textAlign: 'left'}}>{HeadText}</h1>
<h3 style={{textAlign: 'left', color: 'gray'}}>{DescText}</h3>
</div>
<div style={{display: "flex", marginTop: "10%", justifyContent: "center"}}>
<Link to="/explore" style={{marginRight: "5%"}}><button type="button" class="btn btn-primary btn-lg" style={{width: "100%", margin: "auto"}}>Explore</button></Link>
<Link to="/create"><button type="button" class="btn btn-outline-primary btn-lg" style={{width: "100%", margin: "auto"}}>Create</button></Link>
</div>
</div>
<div className="div-right" style={{float: 'right', width: '55%', padding: '3% 3% 3% 3%', margin: 'auto'}}>
<img src="https://www.coindeskkorea.com/news/photo/202103/73065_10919_4125.jpg" width="100%" height="100%" alt='beeple_nft_img'></img>
</div>
</div>
);
}
export default Home;
사이트의 메인 화면으로 화면 우측에는 NFT 이미지와 좌측으로는 /Explore
페이지와 /Create
페이지로 이동 할 수 있는 버튼을 배치하였다. 버튼의 CSS Style
은 Bootstrap를 활용 하였다.
Explore.js
// Explore.js
import React from 'react';
import TokenList from '../components/TokenList';
const Explore = () => {
return(
<div>
<TokenList />
</div>
);
};
export default Explore;
각 NFT의 정보를 불러와서 리스트에 나타낼 수 있도록 해준다. 하위 컴포넌트로 TokenList.js
와 Erc721.js
를 불러온다.
Create.js
// Create.js
import React from 'react';
const Create = () => {
return(
<div style={{justifyContent: 'center', width: '50%', padding: '3% 3% 3% 3%', margin: 'auto'}}>
<div>
<h3>사진 등록</h3>
<button class='btn btn-outline-primary btn-lg' style={{width: '100%', height: '450px'}}>
<i class='glyphicon glyphicon-picture' style={{fontSize: '500%'}}></i>
</button>
</div>
<div>
<h3>이름*</h3>
<input style={{textAlign: 'left', width: '100%'}}></input>
</div>
<div>
<h3>외부링크</h3>
<input style={{textAlign: 'left', width: '100%'}}></input>
</div>
<div>
<h3>설명</h3>
<input style={{textAlign: 'left', width: '100%', height: '175px'}}></input>
</div>
<div style={{paddingTop: '5%'}}>
<button type="button" class="btn btn-primary btn-lg" style={{width: "100%"}}>등록</button>
</div>
</div>
);
}
export default Create;
input
형식으로 사용자의 NFT 등록시 입력하는 값을 받아서 구현 할 수 있도록 구현하였다.
목업을 만드는 정도였지만 지금까지 배웠던게 다 까먹은 느낌이였다. 특히 구현하지 못한 몇몇 부분(web3.js로 컨트랙트 연결, NFT 리스트 구현 등)이 너무 아쉬웠다. React는 아직도 어려웠지만 이번 프로잭트를 통해서 조금 친해진(?) 느낌이였고 web3.js
도 쓰는 것에 익숙해져야겠다는 생각이 들었다.