프로젝트 1에 이어서 2주간 이어지는 프로젝트 2가 진행되었다.
4명의 팀원이 모여서 인센티브 기반 토큰 이코노미를 적용한 커뮤니티 웹 서비스를 개발하는것이 프로젝트의 메인 주제고, 클라이언트, 서버, 스마트 컨트랙트로 업무를 분담해 개발을 진행했다.
내가 이 중 내가 맡은 부분은 백엔드(서버)와 컨트랙트 부분이다.
인센티브 기반 커뮤니티 구조
기본적인 서비스는 인스타그램 서비스를 차용해서 제작하였다.
사용자는 피드를 작성하고, 좋아요를 누르거나 새로운 글을 발행하는 등 커뮤니티를 활성화하는 action을 취했을 때 컨트랙트가 보유한 토큰이 지급되는 간단한 방식이다.
추가적으로 글을 발행해서 획득한 토큰을 사용할 수 있는 마켓을 만들어 NFT를 구매할 수 있도록 하였다. 사용자는 본인이 소유한 토큰을 사용해 NFT를 발행하고, 다른 유저가 발행한 NFT를 구매할 수 있는 방식이다.
NFT 발행은 ERC-721 컨트랙트를 사용하였고, 보상으로 지급되는 토큰은 ERC-20 토큰을 사용하였다.
기술 스택
1. 프론트 : React / Metamask / NFT Storage
2. 서버 : Node.js / MongoDB / Passport.js / Mongoose / Express
3. 컨트랙트 : Solidity / Truffle / Ganache / Mocha + Chai / Openzeppelin
인센트브 커뮤니티 프로젝트 결과
컨트랙트
처음에 접했을 때 컨셉 자체가 명확하기도 했고, 기존의 프로젝트 1을 진행하면서 NFT 발행은 진행했었기 때문에 수월하게 접근할 수 있었다. 하지만 Marketplace라는 1개의 컨트랙트에 ERC-721 토큰과 ERC-20 토큰을 생성하는 컨트랙트를 동시에 담아야 했기 때문에 이 과정에서 Class 형식으로 컨트랙트를 작성하는 방식을 구글링을 통해 학습한 후 진행했다.
이번 프로젝트에서 내가 담당한 부분은 백엔드와 컨트랙트 부분이었다. 컨트랙트를 작성하면서 저번 프로젝트에서 아쉬웠던 부분 중 하나가 테스트 코드를 작성해보지 못한게 한이 되어 이번에는 테스트 코드를 작성하였다.
컨트랙트 테스트 코드를 작성하는 방법은 2가지가 있다. Truffle에서 공식적으로 지원하는 언어는 Solidity와 Javascript다. Javascript는 Mocha, Chai 테스트 프레임워크를 사용하였고, Solidity는 컨트랙트 형식으로 코드를 작성해 Assert() 함수를 사용해 테스트 코드를 작성한다. 기본적으로 사용방법은 유사하지만 JS에서 사용하던 테스트 프레임워크가 역시 손에 익어 이번에는 JS로 테스트 코드를 작성하였다.
컨트랙트에서 작동해야 할 함수는 크게 3가지 정도다.
1. ERC-20 토큰 발행 / 교환
2. ERC-721 토큰 발행 / 교환
3. 현재 발행된 NFT 갯수 확인 및 tokenURI 확인
ERC-721 토큰을 매매하기 위해서는 NFT를 발행한 소유자가 Marketplace에 매매권한을 위임해주는 첫번째 단계와 실제 Buyer가 요청할 경우 transferFrom() 함수를 통해 소유권이 이전되는 2단계로 구성된다. 즉 NFT를 발행한 소유자는 발행과 동시에 Marketplace에 매매권을 위임하는 로직이 필요하다.
테스트 코드는 총 4단계 걸쳐 작성하였고, Mocha + Chai 테스트 프레임워크를 통해 작성하였다.
작성된 컨트랙트는 Ganache + Truffle로 구성한 로컬 환경에서 테스팅한 후 Ropsten 네트워크에 배포하였다. Truffle-plugin-verify 모듈을 통해 이더스캔에서 Verify까지 한 후 ABI와 Address를 받아왔다. 이제 컨트랙트 작업은 끝이 났다. 사실 여기서 컨트랙트를 완성하고 난 후에는 더 이상 컨트랙트 코드는 건드리지 않았다.
추가로 서버에서 컨트랙트를 붙여서 ERC-20 토큰을 지급해주는 로직을 작성하려 했지만, Infura Endpoint에서 제공되는 Provider에서는 eth_sendtransaction 함수가 지원이 안되어 트랜잭션 서명이 불가능하다는 충격적인 정보를 얻게 되었다. 결국 프로젝트의 컨셉이 대거 수정되어 프론트에서 모든 서명을 하고 컨트랙트는 Ropsten에 올리는 것으로 진행했다.
Server
서버는 Restful API 형식으로 작성되었다. 우선 피드를 작성하고 좋아요를 달 수 있는 Post와 사용자가 로그인한 후 지갑 주소를 연결해 Post를 관리하고 발행한 NFT를 관리할 수 있는 User, 마지막으로 NFT Model 까지 총 3개의 스키마를 제작하였다.
데이터베이스는 MongoDB를 사용하였다. 누구나 테스팅 환경을 가볍게 구성할 수 있도록 Atlas를 사용할 수 있는 점이 가장 큰 이유였다. MongoDB ODM으로는 Mongoose를 사용하였다. 컨트랙트와 다르게 API는 충분히 고민하고 스키마를 작성하고, Router와 Controller로 이어지는 MVC 디자인 패턴을 구성했지만 이 후 프론트와 붙이는 과정에서 계속 수정이 되었다.
User Schema의 경우 id, username, email 정도만 Required 속성을 주고, 나머지는 주지 않아 수정하는데 그리 어려움은 없었지만, 좀 더 고민하고 완벽한 스키마를 작성하면 어땠을 까 하는 아쉬움이 남는 부분이다.
로그인 부분은 로컬 로그인과 구글 OAuth 2.0 로그인 기능을 붙였다. 로그인의 경우도 passport.js 모듈을 사용해서 손쉽게 작성할 수 있었다. session cookie를 사용해 적용하는 방식이 서버에 다소 무리가 될 것 같다는 직감이 들었지만 가벼운 프로젝트다 보니 세션쿠키를 사용해 로그인 기능을 구현했다.
로그인 플로우는 우선 사용자가 로컬 혹은 구글 OAuth 2.0으로 로그인을 하면 사용자 DB가 생성되고, 프론트에서 로그인 status를 변경한다. 이 후 사용자가 Wallet을 connect하면 유저 DB에 address를 업데이트 하는 방식으로 진행했다. 로그인 권한을 저장한 세션쿠키를 가진 사용자는 여기서부터 피드를 작성하고, NFT를 발행할 권한을 얻게 된다.
인센티브 기반 커뮤니티 프로젝트 아쉬운 부분
2간 이어졌던 프로젝트다 보니 완성된 프로덕트가 나왔음에도, 더 수정해보고 싶고 아예 다른 컨셉을 적용해서 진행해보고 싶은 욕심이 나는 건 어쩔 수 없나 보다. 코드스테이츠에서 레퍼런스로 던져준 기획안에서는 로컬 블록체인에 컨트랙트를 배포하고 Faucet 기능이나 계정생성, 서버에서 트랜잭션 서명 진행등의 기능이 있었는데 이 부분을 무시하고 Ropsten에 컨트랙트를 바로 배포했던 부분이 아쉬운 부분이다.
결국 프론트에서 사용자가 좋아요를 누르면 1토큰이 지급되는데, 좋아요를 누를 때 마다 서명을 해야 되는 참사가 발생하게 되었다. 처음에 상상만 했던 부분인데, 막상 프로젝트가 완성되고 시연하는 과정을 지켜보니 이건 뭔가 잘못되었다는 생각이 크게 들었다.
결국 하나의 프로젝트를 시작하고 완성할 때 까지 최초의 기획안이 얼마나 중요한지 뼈저리게 느낄 수 있는 프로젝트 2가 되었다. 애초에 기획안에 대해서 크게 고민하지 않고 팀원들 각자의 상상대로 작업을 진행하고 Git Merge를 진행하다 보니 코드가 꼬이는 부분도 많았고, 생각지도 못한 충돌이 발생되는 경우도 많았다.
모든 과정이 그렇듯, 새로운 프로덕트를 만든는 과정에서 분명 배우는 점이 있다. 다음 프로젝트는 마지막 한달짜리 프로젝트가 남아있다. 최초 기획안과 프론트엔드 Mock-up을 통해 조금더 체계적이고 꼼꼼한 기획을 바탕으로 프로젝트를 진행해볼 예정이다.
추가로 팀원들간의 커뮤니케이션이 얼마나 힘든 일인지 다시 한번 깨닫게 되는 프로젝트였다. 팀원이 4명인데, 역할 분담이 제대로 되지 않았고, 1인당 오늘 해야 할일이나 완성된 일에 대한 피드백이 제대로 되지 않아 다른 팀원들은 지금 뭐하는지 알 수 없는 미로에 갇혀버리는 경우가 많았다. 서버도 애초에 내 담당이 아니었는데 어느 순간에 보니 내가 컨트랙트를 다하고 서버까지 하고 있는 상황에 처하게 되었다.
처음 시작할 때 Git Project를 열어 칸반보드를 사용하자는 의견이 모아졌지만, 결국 사용하는 사람만 사용하는 죽은 커뮤니티가 되었고, 후반부로 갈 수록 프로젝트에서 소외되는 팀원들이 생겼다. 팀원들간 좀더 원활한 커뮤니케이션이 필요했고, 더욱 꼼꼼한 기획이 많이 부족했던 프로젝트 였다.
개인적으로 팀원들간 어려운 부분이 있을 때 더욱 활발하게 소통해야 한다는 점을 많이 느꼈다. 프로젝트를 하다 보면 자신이 맡은 부분에 대해 막히는 부분이 분명히 생기고 이 부분에 대해서 팀원들과 같이 논의하면서 방법을 찾아야 올바른 솔루션이 나온다. 아니면 자신만 생각하던 다른 방법으로 솔루션이 나오기 때문에 팀 프로젝트의 방향성과 멀어지는 결과가 찾아오게 된다.
또한 각자 맡은 부분에 대해 오늘 할일 + 오늘 완료한 일 + 피드백 이 세가지는 필수적으로 지켜져야 한다는 걸 많이 느꼈다. 만약 내가 맡은 부분에 대해 오늘 완료하지 못했다면 완료하지 못한 이유, 발생한 이슈들에 대해 공유하고 머리를 맡대어 해결하는 과정이 필수적이다.
회고록 끝!
댓글