1. 프로젝트 설명
프로젝트 주제
드디어 자유주제 프로젝트를 진행했다. 클레이튼 Defi 유동성 풀(Pool) 상품을 한데 모아 가격비교를 할 수 있는 서비스가 우리 팀의 프로젝트 주제였다. 클레이튼 체인의 디파이 생태계 활성화에 기여하고자 했다.
프로젝트 기획의도에 대한 자세한 설명은 노션에 적혀있으며, 소스코드는 깃헙에 올려두었다.
배포 링크는 https://klaypod.com/ 이다.
기술 스택
Front-end | React / Typescript
Back-end | Nest.js / Node.js / Typescript / MongoDB + Mongoose
Crawling | Python / Selenium / Anaconda
Blockchain | Solidity / Truffle / Ganache / Caver.js
Deployment | Docker / Docker-compose / Nginx / AWS EC2 / S3 / CloudFront / Route53
Collaboration Tool | Github / Notion / Discord / Figma / Miro
기술 스택에 대한 선정 이유를 남겨보려고 한다. (백엔드 위주)
- Typescript : 자바스크립트 기반 위에 정적 타입 문법을 추가한 언어이다. 예상치 못한 오류를 야기하기 쉽고 디버깅에 애를 먹게 하는 자바스크립트의 단점을 보완해준다는 점에서 전부터 배워보고 싶었던 매력적인 아이였다. 자유주제 프로젝트이고, 마지막 프로젝트인 만큼 이번 기회에 해보고싶은건 다 해보고 싶었다. 또한, Nest.js와의 궁합이 좋다고 알려져있기도 하다.
- Nest.js : Express 가 유연하고 빠르게 서버를 띄울 수 있다는 장점이 있지만, 정해진 패턴이나 규칙이 적다는 것은 달리 말하면 협업 시 코드의 통일성이 떨어지고 프로젝트 크기가 커질수록 관리하기 힘들다는 말이기도 하다. Nest.js 는 미들웨어, IoC, CQRS 등 이미 많은 기능을 프레임워크 자체에 포함하고 있어 OOP와 모듈화가 쉽다. 공식문서를 보고 따라할 수 있고 기본 기능 외의 필요한 기능에 대해서는 추가적으로 라이브러리를 적용하거나 직접 구현하면 된다. 보일러플레이트를 제공해주기도 한다. 즉, 팀원 간에 규칙과 라이브러리를 협의하는 시간을 단축시킬 수 있다. 물론 Node.js 기반 프레임워크 중 점유율이 가장 높은 것은 Express 이고 그동안 프로젝트도 Express 로 진행했지만, Nest.js 가 spring 과 비슷하게 개발할 수 있다는 이야기를 들어서 공부해보고 싶은 마음이 있었다. 토이프로젝트가 아니면 언제 하고싶은 기술을 다 넣어서 써볼까, 하는 마음으로 선택했다.
- MongoDB : 그동안 익숙한 것은 RDBMS 였고, NoSQL은 이렇다할 만한 경험이 없었다. 데이터 조회에 복잡한 SQL문 명령어가 필요하지 않았고, 프로젝트 진행 척도에 따라 데이터가 추가될 수도 있었기 때문에 스키마의 구애를 덜 받고 조회 속도가 빠르다고 알려진 MongoDB를 사용해보기로 했다. 또, Atlas의 무료 서비스가 니즈와 굉장히 잘 맞아떨어지기도 했다.
- Mongoose : ORM 처럼 ODM을 사용하면 프로그래밍 코드에만 더 집중할 수 있을 것이라 생각하고 결정했다.
시스템 아키텍처
메인 서버이자 화면을 담당하는 서버는 klaypod.com 도메인을 갖고, 클라이언트의 요청에 따라 기능을 수행하는 API 서버는 api.klaypod.com 이라는 서브도메인을 갖는다. 도메인 관리는 AWS Route53을 사용했다. 클라이언트 서버는 정적 파일로만 구성해서 관리해줘도 충분하기 때문에 AWS S3를 이용했으며, S3 자체만으로는 속도가 느리기 때문에 CloudFront의 캐시 기능을 활용해 속도를 개선해 사용자 경험을 높이고자 했다. 조회기능만 있어 HTTP도 무난하게 동작할 수 있겠지만, 추후에 확장될 기능과 보안을 위해 HTTPS로 배포했다. HTTPS 프로토콜 적용또한 AWS의 서비스를 이용했으며, Certificate Manager 를 통해 SSL/TLS 인증을 적용해주었다.
데이터베이스는 따로 배포할 필요가 없이 클라우드(무료)로 사용할 수 있는 mongoDB Atlas를 사용했으며, ODM인 mongoose를 사용했다.
블록체인의 경우 클레이튼 테스트 네트워크인 바오밥 네트워크에서 진행됐으며, 이더리움의 web3.js 와 같은 역할을 하는 caver.js 라이브러리를 사용해서 지갑을 연결하고 컨트랙트와 상호작용을 했다.
화면단은 리액트로 개발됐으며, 정적파일만으로도 충분하기 때문에 AWS의 S3 서비스를 사용해서 배포했다. 다만 S3 만으로는 느리다는 단점이 있기 때문에, 속도 개선을 위해 CDN인 CloudFront 서비스를 함께 써줬다. 또한 CloudFront로 배포하면서 Certificate Manager를 이용하여 SSL 인증을 수행하고 HTTPS 접속이 가능하게끔 해줬다.
API 서버는 EC2의 우분투 20.04 환경을 기반으로 배포했다. API 기능이 담긴 Nest.js 서버는 환경에 구애받지 않고 배포 가 편리하도록 도커 컨테이너를 기반으로 개발환경을 구축하고 배포했다. 도커파일로 매번 실행하는 것은 불편하기 때문에 docker-compose 파일도 작성했으며, 2-stage build 도커라이징을 하여 개발환경(development)과 배포환경(production)을 별도로 관리하고자했다. 약 4주 간의 프로젝트 기간동안 staging 단계에서 테스트할 시간은 없었기 때문에 단순화하여 개발환경과 배포환경 둘로만 나누게 되었다.
화면단 서버가 HTTPS 로 구축되어있기 때문에 정상적인 통신을 위해서는 API 서버도 마찬가지로 SSL 인증이 필요했다(보안을 위해 당연히 필요한 작업이기도 했다).이전에 HTTPS 서버를 띄우는 실습 과제에서는 mkcert를 이용해서 로컬에 인증서를 저장하는 방식으로 실습을 했지만, 배포의 경우는 다른 방법을 찾아야 했다. 여러가지가 있겠지만 나는 무료로 인증서 발급을 할 수 있는 letsencrypt 를 사용하여 SSL 인증을 받았다. 다만, 유의할 것은 letsencrypt는 90일짜리 단기 인증서라는 것이다. 따라서 자동으로 갱신해줄 필요가 있는데, 나는 certbot 을 사용했다. 둘 모두 도커를 통해 보다 간편하게 설정할 수 있었다.
마지막으로 Nginx를 사용한 이유이다. 앞서 말했듯 우리는 api.klaypod.com 이라는 서브도메인을 통해 HTTP 요청을 날릴 수 있도록 설계했다. 따라서 EC2 인스턴스에서 API 서버를 실행시킨다고 끝이 아니라, 이 서브도메인과 우리의 API 서버를 연결시켜줘야 원하는 의도대로 동작할 것이다. 그리고 클라이언트 서버가 HTTPS 통신을 하기 때문에 그에 맞게 API 서버도 HTTPS 를 구축해야 정상적인 통신이 가능할 것이다. 나는 Nginx의 reverse proxy를 통해 이를 해결해주었으며, 마찬가지로 Nginx 또한 도커 이미지 파일을 사용하여 보다 간편하게 설정할 수 있었다. (배포 과정에서 약간의 삽질이 있었기 때문에 노션에 정리해둔 내용에 살을 붙여서 블로깅할 예정이다.) Nginx를 사용함으로써 얻는 이점은 로드밸런싱, 보안, SSL 암호 인증의 편리함이다. 현재 프로젝트는 API 서버가 하나 뿐이지만, 서브도메인이 늘어난다면 로드밸런싱의 역할이 확실히 크게 다가올 수 있을 것 같다. 관련 내용 또한 학습을 위해 따로 블로깅할 예정이다.
나의 역할
백엔드 총괄
나는 팀에서 백엔드를 담당했다. DB 스키마 설계 및 REST API 명세를 설계하고 기능 구현을 맡았다. 프로젝트의 메인 기능인 스캐너 조회 관련 기능을 구현했다. 구체적으로 말하자면 프로젝트의 TVL 순으로 정렬 조회하는 기능, 페어 예치 상품들을 조건을 걸어(APR/TVL순) 정렬 조회하는 기능, 페어 예치 상품 목록에서 프로젝트 명으로 필터링해서 볼 수 있는 기능, 그리고 페어 상품 명을 검색할 수 있는 키워드 검색 기능을 구현했다. 배포 사이트에는 정렬 조회만 보이지만, 필터링 및 검색 api는 구현되어있다(프론트 팀원이 혼자여서 나머지 기능까지 구현할 시간이 부족했다. 혼자서 정말 고생 많이 하셨다...). 또한 커서 기반 페이지네이션을 구현하고 적용하기도 했다. 마지막 주에는 도커 컨테이너 기반의 서버 배포 작업을 진행하고, 프론트 서버와 API 서버 연결 과정에서 만난 이슈들을 해결했다.
이슈 해결
개발 및 배포 과정에서 겪는 각종 이슈들을 해결하고 TIL 혹은 팀 노션의 이슈관리 페이지에 기록했다. DB 연결 및 서버 초기 세팅, git conflict, 서버 에러, Nginx 등등이 있었다. 다른 팀원의 git 버전이 꼬였을때 화면을 공유하고 함께 해결해주기도 했다. 프론트-백 연결 작업 과정에서 이슈들(CloudFront 캐시 문제, SPA 배포 이슈, HTTPS 배포 이슈 등)을 해결하고 프론트 팀원과 소통하는 과정에서 많은 것을 배웠던 것 같다. 로직을 이해하기 쉽게 문서화해서 전달해주는 것, 프론트 측이 테스트하던 중 찾은 버그를 빠르게 해결해서 다시 알리는 것, 진행 상황을 서로 공유하는 것 등등. 마지막 프로젝트인 만큼 세 개의 프로젝트 중 가장 노련했고 얻은게 많았던 것 같다.
문서화
회의를 진행할 때마다 회의기록을 남겨두기도 했다. 팀 노션 템플릿을 세팅해 소통을 보다 편리하게 하려고 노력했고, 특히 API 명세 문서를 최대한 구체적으로 적으려고 많이 노력했다. 문서만 보고도 바로 이해할 수 있어야 프론트-백 협업이 원활히 진행될 것이라 생각했기 때문이다. 한 가지 아쉬운 점은 이슈 관리를 노션에서 진행한 것이다. 프로젝트를 마무리하고 보니 노션의 보드 보다 GitHub의 칸반보드 기능을 이용하는 것이 커밋과 연동도 되고 더 보기 좋았겠다는 생각이 든다.
2. 회고
초기 목표와 결과물 비교
초기 목표
Bare Minimum
- 스캐너기능(정렬 조회, 필터링), 도커활용, 스왑
Advanced
- 검색, 스테이킹, 클립 연동, 배포
결과물
Bare Minimum
- 스캐너O, 도커O, 스왑X
Advanced
- 검색O, 스테이킹X, 클립 연동X, 배포O
스왑을 제외한 Bare Minimum으로 설정한 최소 목표는 다 달성했고, Advanced로 넣었던 검색 기능과 배포에 성공하는 성과를 얻었다.
직면한 어려움
- 일정조절실패로 블록체인 스왑 스테이킹 기능을 완성하지 못한 것...
- 이슈 중 이미지 엑박 에러 이슈를 아직 해결하지 못한 것
- 담당업무 분배
문제 해결
- 배포하면서 해결했던 문제들 (SPA 다이렉트 접속 access denied, https 적용해서 서버 배포하기 등)
- MongoDB Atlas 연결 주소 -> 공식 문서의 중요성
- 커서 기반 페이지네이션 로직 구현
- Dockerfile 작성
- 레포지토리 패턴 적용하면서 있었던 이슈 해결
- Git 버전 관리 이슈 해결
배운 점 / 성장한 점
- 프로젝트 2 에서는 검색기능을 advanced 로 설정하고 구현해보지 못하고 끝났는데, 이번 프로젝트에서는 필터링과 검색기능을 구현했다! 구현하고나서 느끼는 성취감이 너무 재밌다. 실무에선 별 것 없는 레벨일테지만 난 아직 경력이 없고 처음이었으니까..뿌듯해해도 된다고 생각한다!
- offset 기반이 아닌 커서 기반 페이지네이션을 구현한 것!
- 배포까지 해보고, 도커도 사용해본 것!! 도커로 배포하고 도커허브도 사용한것!!! 도커 너무 재밌고 신통한 녀석이다!!! 이번에 settings 권한이 없어서 시도는 못했는데 github action으로 자동배포화도 꼭 해보고 싶다. 개인레포에서 연습해볼것ㅎㅎ
- 협업에 쓰이는 깃 명령어와 브랜치 전략, 깃 플로우가 프로젝트 시작 전보다 확실히 손에 익게됐다. merge 방법 세 가지에 대해 많이 연습해보면서 좀 더 확실히 할 수 있었고, 더 나은 커밋 히스토리 관리를 고민하면서 많이 성장했음을 느낀다.
- 나의 진로를 확실히하게 된 것 같다. 물론 모두 각각의 매력이 있고 재밌지만 난 프론트보다 백엔드가 재밌다. 스마트컨트랙트보다는 웹 플랫폼/서버가 더 잘 맞는 것 같다!
- 어떻게 하면 좀 더 프론트분과 소통하기 좋은 API 문서로 작성할 수 있을지에 대한 고민을 계속 하다보니 로직도 분명해지고 내가 작성한 로직을 더 잘 설명할 수 있게된 것 같다.
- 부트캠프에서 전혀 배우지 않은 Nest.js 와 typescript를 공부하고 적용해보면서 초반엔 속도가 더디긴 했지만 진행할 때는 굉장히 재밌었다. Nest.js의 초반 러닝커브가 좀 있었지만 그만큼 정통성 있고 이점이 확실히 보장된 디자인 패턴 을 배울 수 있어서 좋았다. 혼자서 적용해보려고 했으면 굉장히 어려웠을 것이다. 디자인 패턴에 대한 공부의 필요성을 느끼는 계기가 되었다. 기존의 언어들에 대한 베이스가 있고 또 공부하는 방법을 터득하게 된 것 같아서 앞으로 또 새로운 언어와 개념들을 배우게 되더라도 금방 배울 수 있을 것 같다는 생각이 든다. 그래도 이정도면 다양하게 다뤄봤으니 하나에 정착해서 더 깊에 파야겠지! 전문적으로 주력언어를 만들어야 다른 언어를 할 수 있는게 강점이 될 수 있다!!
- 또 하나 배운점은 역시 어려워보여도 계속 하면 어떻게든 되게 된다는 점?!
아쉬운 점 / 개선할 점
- 배포 자동화 CI/CD 구축의 필요성을 느꼈다. 레포지토리 설정 권한이 없어서 klaypod 프로젝트로 Git actions로 배포 자동화까지 해보진 못할 것 같고, 따로 개인 플젝에서 자동화를 해보면 좋을 것 같다.
- JWT & OAUTH2 인증 기반 Web2 토이프로젝트도 더 딥하게 다뤄보고 싶다!
- 좀 더 복잡한 DB 구조와 쿼리를 날리고
- 일정상 엑박 이슈 해결하지 못하고 1차 배포한 것.
- 더 복잡한 서비스 모델을 구현해보고 싶고 더 복잡한 DB 설계도 하고싶은 욕심이 듦! 댓글도 쓰고 글도 쓰고 좋아요도 누르고 DB의 인덱스 기능도 공부하고 사용해보고 싶다!
해야할게 너무 많다. 공부하면 할수록 나에게 부족한 점이 더 잘 보인다ㅠㅠ - 메시지큐 사용해서 알림도 보내고! 소켓으로 채팅봇도 만들어보고! 해보고싶은건 참 많은데,,,
- 개발기간 동안 테스트 코드를 작성하지 못한 것. 예전에 교수님이 “테스트 코드를 짤 시간이 없어서 짜지 못하고 개발만 했다”는 변명은 그만큼 본인이 코딩 실력이 없다는 소리라고 하셨는데… 맞는 것 같다. 숙련도 부족의 문제… 프로젝트는 끝났지만, 취준과 병행하면서 테스트 코드를 연습해볼 계획이다!
앞으로의 각오
프로젝트가 끝났다. 뿌듯한 점도 많지만 아쉬운 점도 많다. 아쉽게도 해보지 못했던 기술들을 이어서 보강하고싶다. 테스트 코드를 작성해보면서 유닛 테스트도 돌려보고싶고, 인증인가가 적용된 프로젝트도 하나 더 해보고싶다. 아직 단순한 DB 구조만 설계해봐서, 좀 더 복잡한 DB구조와 쿼리를 다뤄보고 싶기도 하다. 그동안 잠시 손 놓고 있던 코딩테스트도 준비해야 하고, CS 기술 면접도 준비해야하고, 포트폴리오랑 이력서도 좀 더 다듬어야하고... 할일이 태산같다. 나태해지지말고 힘내자!