컴퓨터를 공부하고자 마음먹은지 N일차
[188일차] <연근마켓> 프로젝트 회고 본문
소개
깃헙레포: link
배포링크: yeongn.com
이 세상에 무쓸모는 없다! 라는 모토로 만들어진 프로젝트이다.
오래된 책들, 과거의 추억이 있는 물건, 집안 대대로 전해져 오는 가보 그리고 자신만 가지고 있는 신기한 물건,
나만 알고 있는 정보 등 어떠한 것들도 감정을 받을 수 있는 커뮤니티 성향이 강한 앱이다.
더 궁금하다면 배포링크로 들어가서 한번 이용해보자!
기획단계
우선 배경을 설명하자면, 우리 팀원들은 이전 프로젝트를 함께 했었고, 심지어 합숙도 한번 했다.
그때 너무 하하호호 즐겁게 소통하면서 했기에, 팀원중 한명이 기똥찬 아이디어를 하나 내길래
바로 다음 프로젝트는 그걸로 하자고 그랬다.
그렇게 기획단계가 순조롭게 흘러가나 싶었는데.. 진짜 기획을 진행하다가 하다가 보니까,
너무너무너무 구린거다.
구현할 방향은 잡히지만, 막상 하나씩 와이어프레임을 만들다보니 엄청 구렸다.
사실 나는 구리다는걸 부정하고 이 아이디어대로 만들고싶었다.
하지만 나를 제외한 세명모두 만장일치로 구리다 그랬고 다시 생각해보니 머릿속으로 그려지는 앱이
너무 매력이 없었다.
그래서 다시 아이디어 구상단계로 회귀해서 진행을했다.
그래서 뜬금없이 유튜브를 보는데, 팀원 중 한명(코공)이 즐겨보는 채널인 히스토리 채널의 전당포사나이들을 보던 중,
문득 번쩍하고 떠올라 우리도 저런 전당포같은 앱을 하나 만들어보자!
하고 우스겟소리로 한번 던져본 말이 여러번의 기획과정을 통해 지금의 결과물이 되었다.
스택구성
Nestjs
우선 이전엔 express만 사용했다. 충분히 mvc모델을 활용하여 나름 잘 정리했지만,
좀 더 oop를 적용한 서버앱을 만들어 보고 싶다는 생각을 하다가 nest를 채택하게됐다.
nest의 단점은, nodejs의 장점인 빠르고 간편하게 자유도높은 서버앱을 만들어준다는 특징을 못살린다.
그렇지만 어느정도 static한 규격이 있기에 더욱 협업에 유리할거라 판단을 했다.(애초에 규모있는 서비스를 위한 프레임워크란다.)
module과 controller 그리고 service라는 구조는 생소했지만,
직접 중요한 기능(이미지 업로드, jwt 등) 을 service로 만들면서 재사용성이 정말 높고 간편하단 느낌을 받았다.
그리고 오히려 간편하다. cli를 활용해 기초적인 구조를 잡을 수 있기 때문이다.
cli로 라우터를 생성하고, 만들어진 라우터에 서비스를 달아주면 끝이다.. 정말 간편함
또한 nestjs의 공식문서.. 진짜 다른공식문서들이 보고 배워야 할 정도로 친절하다.
그래서 제대로 nest의 모든 이점은 못살리더라도 한번 적용은 해보자! 하고 프로젝트에 활용했다.
fastify
이 부분은 내가 프로젝트가 끝나갈 때 즈음 한번 포스팅을 한적이 있다.
nestjs의 기본어댑터인 express가 아닌 fastify를 사용한 이유는 바로
빨라서이다.
단순히 빠르고 좋다고 기술을 채택한 내 오만함이 여러 곤란함을 일으켰다.
아무리 nest의 친절한 공식문서라 각 예시마다 fastify를 적용할거면 어떻게 할건지 친절하게 알려준다 그러나
약간이라도 더 딥한 부분은 fastify를 적용할거면 어느정도 니가 알아봐라 하는 성향이 강하다.
애초에 fastify는 빠르다고 좋다좋다 하지만, 정작 커뮤니티가 그렇게 넓지않다.
조금만 더 복잡한 문제로 파고들면 정보를 찾기가 매우 힘들고,
fastify자체의 공식문서 또한 가독성이 매우 떨어졌다.
위에 링크로 걸어논 fastify를 적용한 후기를 한번 봐주자..!
Redis
우리가 만들 요청 중 가장 동시에 처리해야할 요청이 많은게 무엇인지 생각했고,
이전 프로젝트의 좋아요 요청의 속도가 이질감이 느껴질 정도로 느려서, "좋아요"데이터를 캐싱 하기로 했다.
RDBMS까지 가서 데이터를 처리하는데 걸리는 속도보다 압도적으로 빠르다.
배포전략
Docker
docker는 우리가 만든 서버 / 클라이언트 앱을 이미지로 빌드시켜주고 컨테이너화시켜주는데,
사용해보니 확실히 클라이언트에서 빌드한 이미지를 바로 받아서 로컬환경에서 테스트하기도 용이했고, 환경이 달라서 생긴 이슈는 없었다.
우선 이번 프로젝트가 시작되기 전에 꼭 달성하고 싶었던 목표는 자동화 배포 였다.
우선 기업단위도 아닌 고작 학원 수강생의 프로젝트에 왜 자동화배포를 할려고 했는가?
먼저 팀의 업무효율이 배로 늘어난다.
배포환경을 테스트 하기 위해서 매번 코드수정이 있을때마다 ssh를 통해 원격으로 조정을 한다던가,
s3에 버켓을 배포하고, 클라우드프론트 캐시를 지워주는 등 프로젝트에 수정사항이 있을 때 마다 바꿔야한다.
사실 이게 처음 아예 개발을 시작하는단계에선 별로 중요하지않다. 문제는 리팩토링단계 이다.
리팩토링을 할때마다 저 과정을 반복해야 하는데,
우선 저 과정만 자동화를 시켜놓는다면, 프로젝트가 끝나더라도 수정사항을 계속 고치는데 망설임이 없을거라 생각했다.
배포 과정에서는 자동화를 선택한 이상, 배포환경에 따른 이슈가 없어야하기에 반드시 도입해야 되는 스택이라 생각했다.
도입하고자 마음을 먹고는 Docker와 CI환경에 대한 학습을 위해 프로젝트를 진행하며 인프런에서 바로 강의를 들었다.
ElasticBeanstalk
eb는 정말 aws의 편리함의 집결체다.
EB에 앱이미지를 배포하면 기존 ec2인스턴스를 직접 만들어서,
ssh를 통해 원격으로 접속을 하던 방식과 달리 그냥 이미지를 업로드만 해주면,
이미지를 실행시켜주고 앱의 크기에따라 리소스의 낭비를 줄이기위해 크기를 줄여주며,
부하에 따라서 분산시켜주는 오토스케일링을 지원해준다.
이 모든걸 그저 업로드만 하면 말이다.
CI환경을 구성할 때 역시 ec2를 통해서 배포를 하는것 보다 더 shell명령어를 덜 입력해도 되겠다 라는 생각이 들었다.
시행착오
이 배포단계에서도 굉장히 나를 고민에 빠지게 하고 힘들게 하는 이슈들이 있었다.
travisCI의 불통
사정상 private 레포를 써야하는데, private또한 travis에서 100번의 빌드를 제공해주기때문에,
한번 사용할려고 했는데, 코드스테이츠 조직의 repo라서 내 빌드가 제한되는것이었다..!
그래서 아무조건없이 그저 일주일에 약 50번 빌드가능한 2500크레딧을 주는 circleCI로 순회했다.
travis파일을 어떻게 작성하는지 대강 배웠기에 찾아보면서 하나씩 작성해보는건 그렇게 어렵지 않았다.
배포파일의 버전 나누기
원래는 ElasticBeanstalk에 바로 zip파일을 배포해버리는 식이었다.
그래서 문제가 생겼을 때 사실상 롤백이 불가능한 구조였다.
그래서 인터넷에 검색을 해보니 다들 s3에 업로드를 하고 eb에 보내는것이었다.
그래서 circleCI의 sha1 환경변수를 활용해 모든 zip파일의 이름을 다르게 하고,
eb인스턴스의 버전도 다르게 한 이름으로 설정했다.
그래서 만약 롤백이 필요한 상황이라면, s3버켓을 날짜별로 조회해서,
현재 버전을 이전버전의 파일로 대체 하면 되겠다고 생각을 했다!
고칠점
고칠점이라기 보단, 궁금한 점이 생겼다.
eb에다가 dockerrun.aws.json파일만 zip파일로 보내도 앱이 실행이 될까?
이걸 실험해보는것을 이슈로 둬야겠다.
많이 아쉬운점
유닛테스트던 e2e테스트던 클라이언트와 서버 모두 테스트코드를 짜면서 하고싶었지만,
시간이 촉박했다..! ci/cd환경에서 테스트로직이 없다는게 말이 안된다..!
tdd방법론과 테스트 모듈에 대해 더 학습을 한 후, 반드시 프로젝트나 실무에 적용해보고싶다.
주요구현기능
글작성
그냥 텍스트만 가는 글의 작성이면 별 문제없지만,
이미지 업로드 였다.
express를 통해서 이미지를 업로드 한적도 없었는데,
nestjs 그것도 fastify를 활용해서 이미지를 업로드 해야했다.
s3버켓에 이미지를 업로드해서 그 url을 db에다 갖고있는것 까지는 이해했다.
문제는 s3버켓에 어떻게 업로드 하느냐? 였다.
그래서 fastify image upload라고 구글에 아무리검색해도,
내가 이해할 수 있는 선에서 구현된 코드가 없는것이었다.
다소 절망스러웠지만.. 어차피 fastify도 nodejs기반이잖아 하고 생각하며,
nodejs로 된 코드를 찾아봤다.
이미지를 업로드하는 로직은 클라이언트로 받은 form data중 image파일을,
buffer형식으로 바꿔서 aws s3에다가 업로드 해주는것이었다.
fastify에서는 fastify-multipart라는 모듈을 활용해서 form data를 읽어오고,
이미지파일을 버퍼로 변환시켜줬다.
그리고 변환시킨 buffer파일을 업로드 해서 이미지 url을 받아와서 db에 보관했다.
시행착오
이미지 크기가 1MB가 넘어가면 요청이 막히는 이슈가 있었다.
초기 Multipart설정의 문제인줄 알았는데,
nginx의 body사이즈의 제한 때문에 생긴 이슈였다.
구글링 한 후 후딱 바꿔줬다.
게시글 추천
이 전 프로젝트는 n:m 테이블을 하나 더 만들었다.
좋아요 추천한 데이터가 엄청많거나, 한번에 많은 좋아요데이터가 들어올때,
속도에 영향이 있을 수 있겠다 싶었다.
좋아요는 바로바로 안올라가면 살짝 답답하기 때문이다.
그래서 좋아요 데이터를 캐싱처리 한다는것을 구글에서 보게된다.
RDBMS 까지 갔다가 데이터를 처리하고 다시돌아오는것보다 redis를 활용해 캐싱처리하는게
압도적으로 동시간 처리량이 훨씬 많고 빠르다.
그래서 recommend라는 서비스를 만들고,
SET자료구조를 활용해서 게시글의 고유값을 키로 넣고 user의 고유값을 밸류로 넣었다.
전체적으로 이미지업로드 같은 반복될만한 로직을 서비스로 만들어 재사용성을 높였다.
고칠점
dto의 부재
유효성 검사가 없는건 아니지만,
dto를 통해서 응답해주는값의 타입을 정해줘야하는데 기능을 만들어 내는데에 급급해 제대로 활용을 못했다.
받는값의 대부분이 formdata를 활용하기 때문에 이것의 타입을 어떻게 정해줄지도 한번 고민해봐야한다.
이미지를 수정, 삭제하면 기존 s3버킷에있는 이미지를 삭제하지않음
이것도 역시 빠른 기능구현을 위해 이미지 url만 교체하는 식으로 구현을 했다.
aws-sdk엔 버켓에 있는 파일을 삭제하는 기능도 있으니 한번 활용을 해보자.
매 요청마다 토큰을 verify하는부분을 hook(미들웨어)으로 안뒀음
우선 fastify의 hook에대한 내용을 잘 이해를 못하겠으며,
미들웨어를 사용하는 방식이 express와 다소 달라서 일단은 최대한 덜반복되게 jwt라는 서비스를 만들었다.
이부분을 좀더 공부를 보충해서 hook으로 만들어보는 시도를 해보자.
잔존하는 any타입을 최대한 바꾸자.
이미지 업로드 서비스에서 타입추론하는 부분에 명확한 타입을 정해주니,
확실히 코드의 가독성이 높아졌다.
커뮤니티에서 any쓸때마다 손바닥 한대씩 맞아야된다는 말이 괜히 나온게아니다.
느낀점
아쉬움도 많이 남지만 결국 염원했던 클라이언트와 서버부분 모두 자동화를 성공했기에
동기들 모두 리팩토링 할 부분이 있으면 망설임없이 할 수 있을거라 생각한다.
절친한 백엔드 동료(제킴)가 프로젝트 도중 유학을 준비하게돼서 바쁜 일정으로 코드에 많이 참여를 못했던게 마음에 걸린다.
그럼에도 불구하고 그가 기여한 코드가 프로젝트에서 상당부분 중요하게 작용했다. 해외에서 충분히 훌륭한 개발자가 될거라 생각한다.
같이한 백엔드 동료뿐 아니라 프론트의 구현력에 매번 지켜보며 감탄을 했다.
정말 마음맞는 팀원과 행복하게 프로젝트를 진행해서 다행이었다..!
팀원과 원활한 커뮤니케이션의 힘은 실력을 충분히 넘어선다고 생각한다.
기술스택을 정하는 부분에서 상당부분 나의 오만함이 작용해서 반성을 하게됐다.(fastify를 썼다던지, 충분한 학습없이 nest를 도입하자던지)
기술을 적용했지만 절대 이 스택을 어느정도 할줄안다는 오만함이 생겨선 안되고 경계를 해야되겠다.
'👨💻Personal Log > ⛵️Retrospect' 카테고리의 다른 글
[237일차]입사 후 일주일차 회고 (1) | 2021.05.08 |
---|---|
컴퓨터를 배우고자 마음먹은지 200일을 넘어서면서 (0) | 2021.04.08 |
[180일차]프로젝트에 nestjs fastify를 도입하며 느낀점 (2) | 2021.03.12 |
[157일차]<누가더쎔?> 프로젝트 회고 (1) | 2021.02.18 |
[70일차]70일차 회고 (0) | 2020.11.18 |