OpenSea 클론코딩 (프로젝트 1)

f475·2022년 2월 18일
0
post-thumbnail

0. 프로젝트 소개

클론코딩 결과물 코드(Github)
https://bit.ly/3LCe5zP

OpenSea란?
OpenSea는 Cypto 소장품, NFT(Non Fungible Token) 게임 아이템과 기타 이더리움 블록체인기반의 아이템을 포함한 디지탈 자산의 거래를 지원하는 P2P방식의 마켓이다.
(출처: 해시넷)

이번 팀 프로젝트에서는 OpenSea 사이트를 클론코딩 하였다. Frontend는 React를 사용하였고, Backend에서는 node.js와 web3.js를 사용하였다. 스마트 컨트랙트는 Solidity를 사용하였다. 여기서 나는 Frontend을 맡았다.

1. 페이지 구성


페이지는 아래와 같이 구성하였다.

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

2. 코드 설명

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을 활용하였다.

여기서 useStateconst로 선언해야하지만, const사용시 페이지가 멈추는 현상이 발생하여 let으로 선언하였다.

페이지 이동은 react-router-domLink를 사용하여 각각의 텍스트를 클릭시 할당된 페이지로 이동하게 구현하였다.

ConnetWallet는 비동기 방식으로 구현되었으며 클릭 시 MetaMask와 연결 여부를 묻는 창이 뜬다.

그 외 Link를 사용하면 텍스트 위에 마우스를 올릴시 클릭 할 수 있는지 구분 할 수 있게 마우스 포인터가 바뀐다.

나는 ConnectWalletLink로 구현하기 어려워서 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.jsErc721.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 등록시 입력하는 값을 받아서 구현 할 수 있도록 구현하였다.

3. 회고

목업을 만드는 정도였지만 지금까지 배웠던게 다 까먹은 느낌이였다. 특히 구현하지 못한 몇몇 부분(web3.js로 컨트랙트 연결, NFT 리스트 구현 등)이 너무 아쉬웠다. React는 아직도 어려웠지만 이번 프로잭트를 통해서 조금 친해진(?) 느낌이였고 web3.js도 쓰는 것에 익숙해져야겠다는 생각이 들었다.

0개의 댓글