일 주일은 그리 길지 않은 시간이다! - Opensea Clone [BEB PR1 / 후기]

알락·2022년 12월 30일
2
post-custom-banner

banner

후기

마음다짐

본격적인 프로젝트 기간에 들어가기 전 "하루하루 매일 블로깅하기"를 다짐했는데, 블로깅은 고사하고 프로젝트를 진행하기도 바빴던 주간이었다. 그렇다고 쓰러지기 일보직전의 피로는 없었다. 딱 내가 깨어있는 시간, 그리고 팀원들이 다 같이 모이는 시간만 집중하자는 생각으로 임했다.

팀장을 자원해서 팀을 이끌어보기로 했다. 지금까지 팀원으로서 활동한 경험은 많았다. 팀장의 지시대로 해야될 작업만 하고 결과물을 보이기만 하면 된다. 팀원으로 프로젝트를 진행하게 될 때의 고충도 있긴 하다. 이를테면 나에게 버거운 작업을 맡았을 때는 "왜 팀장이면서 팀원 능력도 파악을 못 해 주시는 겁니까"라는 돼도안됄 원망을 할 때도 있다. 하지만 진짜 문제는 팀이 방향성을 잃고 이렇다할 작업을 하지 못할 때다. 이런 문제를 겪고 있는 팀의 팀원인 나는 참여동기를 잃고 헤매버릴 수 밖에 없다. 이 문제를 주체적으로 해결할 수 있는 팀의 구성원이 팀장이라고 생각했다.

일 주일이라는 시간 동안 NFT 마켓플레이스 Opensea 웹사이트를 클론 코딩하는 프로젝트를 받았다. 처음부터 팀원들과 이번 프로젝트, 그리고 우리 팀의 방향성을 제안했다. 우리는 "다음 있을 두 번째 프로젝트, 세 번째 프로젝트를 위하여, 이 기간 동안 공부를 하자"고 얘기했다. 지금까지는 웹개발에 대한 개념만 이해하고 주어지는 스프린트를 풀어나가는데 급급했지, 자신이 직접 뭔가를 하얀 화면에서 시작해 작동하는 서비스를 만드는 것은 교육 기간 동안 대부분의 교육생에게 처음이었다. 적응을 해야될 필요가 있었다고 생각한다. 그렇다면 모든 팀원들에게 프로젝트가 부담이 되지 않을 토픽은 역시 공부였다.

프로젝트 준비

사실 Opensea 클론 코딩을 많이 얕봤다. 내가 이해하고 있는 개념으로 모두 구현이 가능할 것 같았기 때문이다. 하지만 프로젝트 기간 중 주말이 끼어있어서 느긋하게 필요한 기술들을 살펴보고 있는데 점점 불안감이 엄습했다. Opensea의 주요 기능들이 어려운 기술들을 요하고 있었기 때문이다.

이를테면 Opensea의 "NFT 마켓"이 있다. 특정 NFT를 얼마에 판매할지, 얼마에 구매할지에 대해서 구매자와 판매자가 합의가 되면 스마트 컨트랙트를 통해 NFT와 금액이 이전되는 기능이 있다. 내가 배웠던 NFT는 우선 내가 소유한 NFT를 다른 사람에게 보내는 것 까지는 가능하다. 하지만 내가 소유한 NFT를 보내면서 다른 사람이 소유한 암호화폐를 전송 받는 것은 다른 문제였다.

하지만 내가 이번 프로젝트에서 범한 실책은 여기서 시작된다. 우리의 능력에서 벗어나는 일이라면 다른 형태로 구현을 해야했다. 고집스럽게 "공부하면 된다"라는 마인드로 진행하다보니 무리가 발생했다. 내가 제목으로 "일 주일이라는 그리 길지 않은 시간이다!"라고 적은 것은 이유가 있다. 물론 공부라는 팀의 방향성을 제시했고 중요한 부분임에 틀림 없지만, 남는 것은 프로젝트 결과물이라는 것을 잊지 말아야 했다. 나는 위 기술을 이해하는데만 이틀을 버렸다.

제한된 시간을 숙지하고 그 기간 내에 할 수 있는 기능 구현을 하도록 하자.
기간 동안 할 수 있는 구현 범위를 좁혀나갈 수 있는 것도 능력이다

프로젝트 과정

그 동안 수없이 거쳐왔던 팀장들을 보면서, 나는 이러지 말아야지, 나는 저렇게 해야지 고민을 많이 해왔다고 생각한다. 하지만 직접 팀장의 시선에서 프로젝트를 바라보니 팀원의 입장에서 보던 프로젝트와는 느낌 자체가 달랐다.

우선 팀장은 자신만이 아닌 다른 팀원 모두를 신경써야한다. 본인도 건사하기 힘든 와중에 팀원을 신경쓰는 것은 정말 어려운 일이었던 듯 하다. 지금껏 나를 스쳐갔던 팀장들에게 했던 투정들을 이 자리를 빌어 사과한다. 하다보니 이제 팀원들 모두의 작업 스타일도 다르다는 것도 깨달았다.

팀장이 다른 것 다 제치고 해야 될 것은, 팀원이 어떤 것을 해야할 지 명확히 지시해야 된다 생각했다. 그러다보니 팀원들보다 프로젝트를 몇 발 짝 앞서서 바라봐야 했다. 팀원으로서 활동할 때 혹시나 넘어질까 내 발밑을 보고 걷던 것과는 다르게, 팀장으로서 활동하면서 앞으로 어디로 갈까를 보기 위해 고개들고 앞을 보는 것과 같은 느낌이었다. 고개를 들고나니 수많은 역경이 보였다. 팀원에게 작업을 지시하려면, 우선 내가 먼저 그 작업을 왜 해야하는지 이해가 되어야 했고, 그 과정에서 생길 문제들을 먼저 파악하고 있어야 했다.

기획서 작성의 중요성

그런 의미에서 프로젝트 진행 중에 팀원들에게 미안함을 느낄 때도 많았다. 마음다짐에서 얘기했던 것처럼 팀원들이 헤메지 않는 팀을 만들고 싶었다. 하지만 결국 프로젝트 과정 중 몇몇 팀원들은 본인들이 무엇을 어떻게 해야할지 몰라 전전긍긍하고 있었다. 작업을 지시했어도, 어떻게 해야되는지를 모를 수 있고, 어떤 것을 건드려야할지도 모를 수 있다. 나는 이 때의 교훈으로 처음 기획을 할 때 기획서를 제대로 작성해야한다는 교훈을 깨달았다. 아무리 구두로 잘 설명해도 사실, 필요할 때마다 보고 이해할 수 있는 기획서를 보면서 개발을 해나가는 게 여러모로 장점이 많다.

사례를 들어보려고 한다. 클라이언트에서 목록들을 나열해서 표현하는 부분이 있다. 그리고 보통 해당 목록은 서버 측에 요청을 해서 데이터를 받아와 표현한다. 하지만 해당 프론트엔드를 작업하시는 분들에게 서버 쪽으로 어떤 API를 요청해야 데이터를 가져올 수 있는지를 모른다면 작업을 어떻게 이어나가야 할까? 그래서 보통 기획단계에서 클라이언트에서 서버에게 요청할 수 있는 API들을 정리한 API 문서가 준비된다. 하지만 이번에는 급하게 개발에 뛰어들면서 API 문서 작성을 건너뛰게 돼었다. 어쩌면 이 문서가 작성이 되어있으면 오히려 구두로 설명하는 시간을 줄여 프로젝트 작업을 준비할 수 있는 시간을 늘릴 수 있지 않았을까 싶다.

프로젝트 종료

결국 여러 실책으로 인하여 원래 계획했던 프로젝트 기능 중 반토막 구현을 하게 되었다. 많은 부분 책임을 통감하게 된다. 꼭 실패한 것은 아니지만, 프로젝트 준비 기간에 시간을 들였다면 더 좋은 결과가 있지 않았을까 생각한다. 그래도 다행히 팀원들은 프로젝트 진행하면서 많은 것을 배울 수 있었다고 얘기해줘서 고마웠다. 어쩌면 이번 팀의 방향성이 "공부"였다고 생각하면 해당 목표는 잘 달성한 듯 하다.

하지만 프로젝트는 결국 결과물이 남는 다는 것을 잊지말아야 한다. 일 주일이라는 시간 동안에도 잘 준비를 했던 팀들은 프로젝트 결과물도 남다를 것이다. 이번 결과물에 크게 책임감을 느끼며, 앞으로 있을 다른 프로젝트들은 이번에 얻은 많은 교훈들을 바탕으로 더 좋은 성적을 낼 수 있게 준비하려고 한다.


프로젝트 소개

project introudce

앞서 소개했던 것처럼 이번 프로젝트는 NFT 마켓플레이스 인 Opensea를 분석, 따라서 코드를 작성해보는 것이다. 위는 이번 프로젝트 중 구현됀 결과물을 시현해보는 장면이다.

⌞ 기능 구현

[NFT 민팅]

minting test

Openpool NFT를 쉽게 민팅 가능한 서비스를 제공한다. 이름과, 설명, 그리고 NFT를 발행할 컨트랙트를 선택해준다. NFT로 만들 이미지를 업로드하고 민팅하기 버튼을 누르면 이더리움 테스트넷에서 확인 가능한 NFT를 민팅해준다.
입력된 NFT 메타데이터와 이미지는 서버와 연동되어있는 클라우드 저장소 AWS S3에 저장이된다.

[민팅 순서도]
minting order

[NFT 컨트랙트 생성 및 등록]

create contract test
NFT를 민팅하기 위해서는 컨트랙트가 필요한데, Openpool에서는 새로운 NFT 컨트랙트를 생성하는 서비스를 제공한다. 그리고 여기서 생성된 컨트랙트를 서버에 등록을 하여 NFT를 민팅할 때 선택지 중 하나로 선택할 수 있게 화면을 구성해준다.

[컨트랙트 생성 순서도]
contract order

⌞ 사용 기술 스택

tech stack used

  • Front-End(프론트엔드)
    React를 이용하여 클라이언트의 상태값을 관리하며 컴포넌트들을 다루기 용이하게 개발했다. 또한 Tailwind CSS프레임워크를 사용하여 프론트엔드에서 하는 스타일 작업들을 효율적으로 만들어줬다.
  • Back-End(백엔드)
    자산과 직접적으로 연관이되고 심지어 해당 웹서비스를 통해 자산의 이동이 발생하기 때문에, 사용되는 데이터들을 더 엄격하게 다루기 위하여 Typescript를 도입하였다. Express를 통하여 클라이언트 요청에 대하여 서버에서 할 수 있는 작업을 API의 형태로 제공했다.
  • DBMS(데이터베이스 관리 시스템)
    서버 측 데이터는 MySQL에 저장을 하여 제공하였다. NFT가 민팅될 때 필요한 이미지와 메타데이터는 인터넷 어디서든 언제나 접근이 필요하기 때문에 서버에서 보관하여 리스크를 감수하기보다는, 클라우드 저장소 AWS S3에 보관하도록 하였다.
  • 블록체인
    클라이언트에서는 만들어지는 모든 트랜잭션을 Metamask를 통해서 진행하게하여 클라이언트가 현재 자신이 하고 있는 행위를 쉽게 확인할 수 있게 만들었다. 또한 서버에서는 Web3.js를 이용하여 필요한 온체인 데이터들을 등록한 프로바이더를 통해서 요청을 할 수 있게했다. 진행된 프로젝트에서는 ERC-721 형태의 스마트 컨트랙트를 미리 만들어 놓고 클라이언트의 새로운 컨트랙트 생성 요청에 쓰일 수 있게 만들었다.

⌞ 주요 이슈

지갑 소프트웨어 이용

이 서비스에서 블록체인 관련한 기능을 제공하기 위해 무조건 사용자의 지갑 소프트웨어를 거치고 작동할 수 있게 구현해주고 싶었다. 지갑이 왠만한 UI/UX를 제공해주고 있기 때문에 사용자가 블록체인 상에서 비용이 드는 행동을 해도 미리 지갑의 알림을 통해서 확인할 수 있기 때문이다.

컨트랙트 생성 기능을 예시로 들어보겠다. 컨트랙트 생성을 요청하기 위해서는 클라이언트가 로그인이 되어 있는 상태에서 버튼을 눌러 요청하면된다. 하지만 버튼을 누르자마자 컨트랙트 생성이 실행되는 것보다 지갑에서 해당 트랜잭션을 전송할지를 되묻는 UI를 나타내는 것으로, 예기치 못한 실행에 의한 비용 발생을 클라이언트에서 방지할 수 있다.

클라이언트에서 컨트랙트 요청을 하는 코드는 다음과 같다.

[컨트랙트 생성 요청 함수]

const createContractHandler = async ()=>{
        setIsCreating(true);
        const account = await metamask.request({method:"eth_requestAccounts"})
        const curAccount = account[0];
        if(!curAccount) return;

        const estimateGasfee = await metamask.request({
            method:"eth_estimateGas", 
            params:[{from: curAccount, data : "0x"+openNFTBytesCode.object}]
        })
        
        const createContractTxHash = await metamask.request({
            method:"eth_sendTransaction", 
            params:[{
                from:curAccount, 
                gas:estimateGasfee, 
                data: "0x"+openNFTBytesCode.object
            }]
        })

        const checkTxInterval = setInterval(async()=>{
            const receipt = await metamask.request({
                method:"eth_getTransactionReceipt",
                params:[createContractTxHash]
            })
            
            if (!receipt) return;
            else {
                setAddressToRegister(receipt.contractAddress);
                setIsCreating(false);
                clearInterval(checkTxInterval);
            }
        }, 5000)
    };

대부분의 함수 실행이 메타마스크를 통해서 이루어지는 것을 확인할 수 있다.

이렇다보니 컨트랙트 생성 트랜잭션도 직접 객체를 만들어 메타마스크 요청 시 인자로 전달해주었다. 컨트랙트 생성은 Zero Address로 data값이 포함된 트랜잭션을 보내면 된다. 이때 항상 데이터값 맨 앞에 0x를 붙여줘야 한다는 점을 잘 살펴야한다.

리믹스에서 컴파일된 컨트랙트 파일을 클라이언트에 저장해두고, 컨트랙트 생성 함수를 실행할 때마다 사용가능하게 만들어주었다.

해당 함수의 checkTxInterval 할당 부분에도 작은 이슈사항이 있었다. 트랜잭션을 전송하자마자 영수증을 요구하면 아직 체인에 올라가지 않은 트랜잭션이기 때문에 결과값이 null로 반환되었다. 이 때문에 값이 생길때까지 Interval을 이용하여 다시금 결과값을 확인해주었다.

⌞ 개선사항

  • 우선 아직 기획만 되고 개발되지 못한 기능들이 많다. 이를테면 NFT 리스팅 작업과 NFT 거래를 들 수 있다. 추후 해당 기능들을 마저 개발할 필요가 있다.
  • 로그인 이후의 기능 제공에 관해서 이렇다할 인증 작업을 하고 있지 않다. 현재는 지갑에 저장되어있는 계정을 통해서라도 대부분의 작업을 할 수 있다. 구현을 해놓았지만 아직은 사용처가 없는 현실이다.
  • 사실 프로젝트를 진행하면서도 계속해서 고민해왔던게 내가 쓰고 있는 코드가 다른 스마트 컨트랙트, 다른 지갑 소프트웨어에서도 재사용이 가능할까라는 문제였다. 이 때문에 web3 라이브러리가 제공하는 컨트랙트 객체를 이용하지 않고 직접 해당 컨트랙트에 있는 메소드와 같이 넘겨야하는 인자값을 인코딩해서 트랜잭션을 생성한 이후 sendTransaction이나 call 요청을 해왔다. 하지만 이런 노력에도 불구하고, ERC721 민팅 관련한 함수명은 컨트랙트 제각각이어서(예시: mintNFT, mintingNFT, mint) 아직 개선을 할 필요가 있는 부분이다.

⌞ 맡은 역할과 성과

👤 역할

  • 프로젝트 리더
  • 백엔드 개발
  • Web3 개발

✏️ 기획

  • 프로젝트 개요 작성 및 팀원 역할 분담
  • 프로젝트 리파지토리 초기화
  • 사용 기술 아키텍쳐 작성
  • DB Schema & API doc 작성

💻 구현

  • 서버 mysql 연동
  • 헤더 디자인 및 구현
  • 마이페이지 디자인 및 구현
  • 메타마스크 연동
  • 클라이언트 NFT json URI 생성 및 이미지 업로드 로직 구현
  • 클라이언트 인증 로직 구현
  • NFT 스마트 컨트랙트 배포 트랜잭션 전송 로직 구현

프로젝트로 배운 것

  • 프로젝트를 진행을 할 때 처음 기획을 제대로 하자. 다른 팀원들이 어떤 것을 해야될지 모르고 헤매고 있는 모습을 보면 마음이 아프다. 팀원들이 각자 어떤 것을 해야될지 자동으로 찾아갈 수 있는 작업 체계가 마련되어야 한다 생각한다.
  • 제한된 시간을 숙지하고 그 기간 내에 할 수 있는 기능 구현을 하도록 하자. 기간 동안 할 수 있는 구현 범위를 좁혀나갈 수 있는 것도 능력이다.
  • 그날그날 회의록을 작성하도록 하자. 내가 못한다면 가장 잘 해 줄 수 있는 누군가에게 부탁을 하자. 회의를 통해서 기획이 바뀔 수도 있는데 회의록으로 공유를 하지 않으면 해당 상황을 놓쳐버리는 경우가 생긴다. 다음에는 회의록을 꼭 작성하도록 하겠다.
profile
블록체인 개발 공부 중입니다, 프로그래밍 공부합시다!
post-custom-banner

3개의 댓글

comment-user-thumbnail
2022년 12월 31일

훌륭하게 해 내신 것 같아요.
다음 프로젝트도 기대가 됩니다!

1개의 답글
comment-user-thumbnail
2023년 1월 10일

ㄷㄷㄷ 알락님 발전속도가

답글 달기