[회고] 파이널 프로젝트를 마치며

유진·2021년 6월 14일
3
post-thumbnail

whywine logo

📃 Whywine 프로젝트 소개 노션 페이지

짧고도 긴 파이널 프로젝트가 끝났다. 4주짜리 토이프로젝트라면 길다면 길다고 말할 수도 있는데, 길어진 SR, 새로운 스택 등의 이유 때문에 뭔가 너무 짧게만 느껴졌다.

아무튼, 이번 프로젝트에서 기억에 남는 몇몇 부분들을 회고해보려고 한다.


👀 좋은 아이디어란 무엇일까? - 팀원들을 설득하기

이번 프로젝트는 SR에 거의 5일을 소모했다. 파이널 프로젝트 첫 회의 때 나온 아이디어는 2개였다.

하나는 내가 제안한 "밑줄 대신 그어주는 서비스"로, 종이책에 연필이나 형광펜으로 밑줄 긋는게 꺼려지고, 밑줄 긋고 싶은 구절을 일일이 필사하기엔 너무 번거롭다는 문제점을 해결하기 위한 아이디어였다. 사용자가 밑줄 긋고 싶은 구절을 사진으로 찍으면 구글 비젼 API를 통해 텍스트를 추출해주고, 사용자는 거기에 메모를 하거나, SNS에 공유를 할 수 있게 해준다. 백엔드에서도 구글 비젼 API와 인스타그램 등의 API를 사용해야 해서 할 일이 많았고, 메모를 칸반보드와 유사하게 구현할 생각이었기 때문에 프론트엔드에서도 챌린징한 주제였다.

다른 하나는 "카페 메뉴를 추천해주는 서비스"로, 처음 가보는 카페에서 메뉴를 고민하는 시간을 줄여주는 역할이다. 사용자의 위치를 기반으로 근처 카페 정보를 불러와야 하고, 사용자 취향을 조사하는 기능이 메인이라 이 아이디어도 프론트엔드와 백엔드 모두 챌린징한 주제였다.

두 아이디어 모두 훌륭해서 어떤 서비스를 만들 것인지 우리 모두 고민했다. 투표를 했을 때 1번 아이디어에 두 명, 2번 아이디어에 두 명이 선택하는 바람에 (나도 2번에 투표했다 ㅋㅋㅋ) 쉽게 결정이 나지 않았다.

파이널 프로젝트가 시작한지 사흘 째에도 지지부진 하자, 결국 내가 총대를 멨다. 나는 2번 아이디어를 밀었기 때문에 2번 아이디어를 진행해야 하는 이유로 설득했다. 설득 이유는 다음과 같다.

(현실성)

  1. 1번 아이디어는 (내가 냈지만) 정말 좋은 아이디어이고, 나 역시도 욕심이 난다.
  2. 그러나 파이널 프로젝트 기간은 4주인데, 지금 아이디어를 결정하고 SR을 마무리하면 사실상 확보된 시간은 3주에 불과하다. 우리 팀은 타입스크립트 등 새로운 스택을 사용할 예정이기 때문에 스택 학습 시간까지 고려한다면 남은 시간은 더더욱 부족하다.
  3. 이런 상황에서 챌린징한 1번 아이디어를 선택하여 높은 수준의 bare minimum을 도전하는 것보다는, 좀 더 허들이 낮은 2번 아이디어를 선택하고, bare minimum 구현 이후 다양한 기능을 추가할 수 있도록 확장성 있게 코드를 짜는게 나을 것 같다.

처음에 이 얘기를 했을 때는 좀 쫄았는데, 그래도 팀원분들 모두 동의해주셔서 2번 아이디어로 진행하기로 했다. 그리고 2번 아이디어를 좀더 다듬고 다듬어서 만든 것이 바로 Whywine이다.

결국, 의견이 대립되는 상황에서 아무도 추가 의견을 안내거나, 양보할 의사가 별로 없다면 오히려 내 의견을 좀 더 강력하게 내세워서 설득을 하는 것도 좋은 방법이라는 생각이 들었다. 😁 대부분의 프로젝트는 기간이 정해져있고, 프로젝트 내의 모든 시간은 금처럼 귀중하기에... 그래도 이렇게 적극적으로 설득할 수 있었던 이유는 우리가 앞서서 꽤 오랜 기간 서로의 의향을 물어보고 각 아이디어의 장단점에 대해 많이 얘기해봤기 때문일 것이다. 그래서 내 설득에 대해서도 다른 팀원들이 수긍했던거고!


🦄 Database 설정 - 스택 선정 방식

2주 프로젝트에서는 DB는 MySQL, ORM은 Sequelize를 사용했었다. 그래서 2주 프로젝트 종료 이후 백엔드 팀원인 재송님과 NoSQL을 사용해보자고 얘기했었고, SR 과정에서 MongoDB로 스키마를 구성했었다. 그런데 다음과 같은 피드백을 받았다.

  1. 위와 같이 조금 더 CRUD에 신경을 쓰시게 된다면, API도 많이 변경 사항이 있을 것으로 예상됩니다. put, delete등의 다양한 메소드를 사용하시게 될거고, 그렇다면 DB의 변동사항이 생기게 됩니다. 특히 몽고 디비같은 경우, 한 와인의 데이터가 저장된 도큐먼트를 삭제하고 동일한 와인 데이터를 다시 생성하면, 다른 ObjectId가 만들어지게 됩니다. 그런 경우, likes, tags에 들어가 있는 와인의 ObjectId도 변경이 될텐데, 이 때는 어떻게 처리를 하면 좋을지도 고민을 해보셔야합니다. 몽고디비는 쓰고 읽기 편하고, 관계형 데이터보다 빠를수도 있지만 관계성을 두고 생각한다면, 어쩌면 sql이 더 좋을지도 모른다는 말씀을 먼저 드립니다.

실제로 우리 서비스의 경우, 유저와 태그, 유저, 리뷰 테이블이 다대다 관계를 복잡하게 갖고 있기 때문에 데이터가 엄격하게 관리되어야 했다. 그래서 형식에 자유로운 MongoDB보다는 엄격하게 형식 제한을 하는 MySQL이 더 적합해 MySQL을 사용하기로 결정했다.

처음 DB를 선택할 때는 "이거 한번쯤 사용해보고 싶었으니까 사용해봐야지"라는 안일한 마음가짐으로 스택을 선정했는데, 아무리 작은 토이프로젝트라도 "스택을 위한 스택 선정"은 바람직하지 않다는 것을 깨닫게 되었다. 그리고 다시 MySQL로 돌아오게 되면서 SQL과 NoSQL의 차이를 다시 한번 되짚어보게 되는 계기도 되었다. 😁 study_log - 관계형 Vs. 비관계형


🦕 TypeORM의 두가지 패턴

MySQL을 선택하게 되면서, 2주 프로젝트 때 사용했던 Sequelize를 계속 사용하기엔 타입스크립트 지원이 미비한 듯 해서, ORM으로 TypeORM을 선택하게 되었다.

TypeORM은 Active Record 패턴과 Data Mapper 패턴 두 가지가 있다. 공식문서에 따르면, AR 패턴은 작고 단순한 구조의 데이터베이스에 사용하기 좋고, DM 패턴은 유지보수하기 편하기 때문에 거대하거나 복잡한 데이터베이스에 사용하기 적합하다.

우리가 피드백을 받아 SR을 막 마쳤을 당시에는 테이블 구조가 단순했다. 일단 테이블이 네개밖에 없었고, 테이블 간 관계도 약했다. 그래서 "작고 단순한 구조의 데이터베이스"에 걸맞는 AR 패턴을 사용하기로 했다.

그런데, 본격적인 스프린트를 시작하고, 회의를 거쳐 몇가지 기능들이 추가되면서 (ex. 리뷰) 조인테이블을 포함해서 테이블이 거의 세배 가까이 늘어났다... 그리고 이러한 상황과 겹쳐서, TypeORM에 대해 공부를 계속 하다보니 AR 패턴이 정말 좋은 ORM 패턴인지 의문이 들었다.

결론적으로 말하자면 위의 두가지 이유로 인해 AR 패턴에서 DM 패턴으로 리팩토링을 진행했다. AR 패턴에서 내가 가장 찜찜했던 부분은 AR 패턴이 DB와 JS 객체를 매핑해준다기보다는, DB 자체를 통째로 가져와서 객체 형태로 보이게 한다는 것이다. 한마디로, 매핑이 아니라 그냥 그대로 가져오는 것이고, 심지어 그대로 가져온 이 엔티티를 별도의 레이어 없이 하나의 레이어에서 CRUD를 수행한다는 것이다. DB 엑세스 레이어와 비즈니스 로직 레이어가 분리되지 않는다는 점에서 이걸 ORM이라고 부를 수 있는지 의문이 들었다. 🤔

반면 DM 패턴같은 경우에는 이러한 문제들을 해결해준다. DM 패턴의 mapper는 DB와 JS 객체를 각각 독립적으로 유지시켜 주면서 양 레이어에서 데이터를 이동시켜주기 때문이다. DM 패턴은 안정성이 좋아 유지보수에 편리하기 때문에 확장성을 중요하게 생각하는 이번 프로젝트에 좀 더 적합하다고 판단했다.

다행히 TypeORM에서 AR 패턴과 DM 패턴은 문법상 큰 차이가 없기 때문에 리팩토링 하는데 많은 시간이 소요되지는 않았다. 또 이번에 TypeORM을 도입하게 되면서 두 가지 아키텍쳐 패턴을 배울 수 있었다는 점에서 만족스럽다. DB의 세계는 정말 넓고 다양한 것 같다.... (공부할게 산더미 😆)


🐙 Git 사용하기 1 - Git Workflow 지키기

지난 2주 프로젝트 회고 시간에 엔지니어님께서 보여주신게 있었다.

바로 깃헙 네트워크 탭!

아래는 지난 2주 프로젝트의 네트워크 그래프이다.

mindcaptor network graph

이 네트워크 그래프는 프로젝트의 커밋 기록에 따른 흐름을 보여주는 그래프이다. 보시다시피, 커밋들이 아주 들쑥날쑥하게 아무런 규칙성도 없이 이어지고 있다.

엔지니어님은 깃 워크플로우, 브랜치 규칙, 커밋 규칙을 지키는 것이 매우매우매우 중요하고, 자꾸 커밋을 할 수록 코드가 사라지는 건(우리가 겪은 문제였다) 커밋이 꼬이면서 merge하는 과정에서 사라지는 것이라고 말씀해주셨다. 우리는 코드가 사라지는 문제를 진절머리나게 겪은 후라, 이번에는 브랜치 규칙과 커밋 규칙을 정했다.

# 커밋 규칙

[타입이름]: 내용 / #issueNumber

[타입 이름들]
기능: 새로운 기능에 대한 커밋
디버깅: 버그 수정에 대한 커밋
테스트: 테스트 코드 수정에 대한 커밋
스타일: CSS 관련 코드 수정
문서: 문서 수정에 대한 커밋
# 브랜치 규칙
1) 브랜치 이름 규칙

- master
    - dev
        - feature_#000_mainPage
        - hotfix_#111_commentError
        - test_#003_어쩌구
        - (같은 기능을 하는 새로운 브랜치를 만든 경우) feature_#000_mainPage_1
        - (같은 기능을 하는 새로운 브랜치를 만든 경우) test_#003_어쩌구_2

2) 브랜치 구조

- master
    - dev
        - feature1 (내 아래 병합시키고 push `git push origin feature1`)
            - feature 1-1
            - feature 1-2
            - fix 1-3
        - feature2
        - feature2_1

3) 기존에 작업하던 branch가 있는 경우

1. local dev에 `git pull upstream dev` 실행
2. local dev에서 branch를 딴다 (`git checkout -b feature1_1`)
3. 기존 feature1 브랜치를 commit하고, feature1_1 브랜치에 병합 (feature1_1 브랜치에서 `git merge feature1`)

중요한 부분은 마지막 기존에 작업하던 브랜치가 있는 경우이다. 이 경우 우리는 dev 에서 pull을 진행하고, A작업에 대한 새로운 작업 브랜치 A-2 를 파서 기존 작업 브랜치 A-1을 머지하는 방식을 사용했다.

사실 이 방법이 맞는지는 모르겠지만, 이 방법을 사용했을 때의 장점은 브랜치 구조의 질서를 해치지 않으면서 내 작업을 비교적 안전하게(?) 보호할 수 있다는 장점이 있다고 생각한다.

결과적으로 파이널 프로젝트의 네트워크 그래프는 다음과 같은 모양이 되었다.

whywine network graph

(중간에 프론트엔드 팀원 분이 코드를 바로 가져가셔서 저렇게 되긴 했는데... 그래도 뭔가 훨씬 일관성 있고 깔끔해보이지 않나요? ㅎㅎ)

브랜치와 커밋 관리를 빡세게 하면 작업속도가 느려지지 않을까 내심 걱정했는데, 머지할 때도 심각한 컨플릭은 일어나지 않아서 오히려 작업 속도도 빨라졌다!

이번에 팀 프로젝트를 하면서 현업에서는 어떻게 브랜치 관리를 하는지 궁금해지기도 하고, 시맨틱 버저닝 방식으로 좀 더 명시적으로 버전 관리를 했다면 네트워크 모양이 어떻게 됐을 지 궁금하기도 했다. 언젠가 진짜로 서비스를 하게 되면 시맨틱 버저닝 방식이나 더 섬세한 방식을 쓸 텐데 재밌을것 같다.

한편으로는 이번 프로젝트에서 Prettier 같은 코드 데코레이팅 툴을 사용하지 않아서 엔터 줄이나 따옴표 등에서 컨플릭이 간간이 일어난게 살짝 아쉬웠다. eslint와 prettier를 사용하려고 했는데 팀원분들 중에 잘 설치가 안되는 분들이 계셔서.... 😓 실제로도 eslint와 prettier를 설정하는게 어렵다고 한다. 다음에 또 팀 프로젝트를 진행한다면 그 때는 꼭 써보는것으로!!


🐙 Git 사용하기 2 - Issue, Milestones, Kanban을 사용한 일정관리

Issue 탭, 마일스톤, 칸반 모두 지난 2주 프로젝트에서도 사용했지만 사실 시간이 너무 부족해 제대로 관리되지 못했었다. 그래서 이번 기회에는 꼭 제대로 써보자고 단단히 마음을 먹은 상태이기도 했다.

Issue 탭에서는 태스크 이슈를 설정하고, 마일스톤으로 스프린트 별로 해야 할일을 나눠 Due date를 설정했다. 그리고 태그로 해당 이슈를 완료하는데 소요될 예상 시간과 어느 파트의 일인지 함께 명시해놔서 서로 어떤 작업을 하는지 공유하는 데 도움이 되었다.

특히 마일스톤을 설정해두면 due date를 명시적으로 확인할 수 있기 때문에 다들 일정을 지키려고 좀 더 노력하게 되는것 같다. 그리고 이슈를 PR과 연결해 자동으로 닫게 해주는 기능도 좋았다 🙂

issue

또 칸반보드를 좀 더 열심히 사용했었다. 칸반보드는 내가 오늘 해결할 태스크 카드를 정리해 어떤 일을 할 예정이고, 하고 있고, 완료했는지 바로 보여주는 역할을 한다. 칸반보드는 내가 오늘 해야 하는 일을 정리해주는 것 뿐만 아니라, 현재 내 동료가 무슨 작업을 하고 있는지 바로바로 파악할 수 있다는 장점이 있다. 그래서 내 일이 좀 더 빨리 끝났을 때나 동료에게 도움을 요청하기 전 동료의 상황을 한눈에 체크할 수 있어서 굉장히 편리했다.

kanban

칸반보드에는 이슈 관리 뿐만 아니라 개인 회고와 일주일에 두번씩 작성하는 팀 KPT 회고도 한 곳에 모아볼 수 있게 정리해두었다.

devlog kanban


📃 문서화

팀 프로젝트에 있어서 개인 목표를 "팀 프로젝트 툴을 최대한 활용해보기"로 정했었다. 그래서 문서화나 기록은 거의 도맡아서 했었다ㅋㅋㅋ 그래서인지 개인 목표를 어느정도 달성한 것 같다. 사실 프로젝트를 하기 전에는 문서화를 그저 백업용으로만 생각했었는데, 플젝을 진행하면서 문서(그리고 각종 프로젝트 툴들)는 아주 중요한 소통 도구라는 걸 느꼈다. 앞서 언급한 칸반보드나, 프론트엔드 파트와 이야기를 할 때는 API 문서나 플로우 차트를 매개로 하는것 등등... 특히 팀 노션페이지에 회의 내용을 꼼꼼히 작성해 쉽게 이야기 도중 딴 길로 샐 때마다 원점으로 돌아오게 해주는 역할을 했었다.


💫 마치며

원래 팀프로젝트를 별로 좋아하지는 않았다. 팀원의 무임승차(?) 같은 문제는 아니고, 오히려 내가 누군가에게 무임승차 하게 될까봐 차라리 모든 걸 혼자 하는걸 선호하는 편이었다.

그래서 이번 프로젝트가 더욱 특별했던 것 같다. 이런 저런 상황 속에 부딪히면서 결국 사람이 두 명 이상 모이게 되면 필연적으로 상대에게 민폐를 끼칠 수 밖에 없는 상황이 온다. 우리 모두 인간이고 인간은 늘 실수를 하기 마련이니까. 팀 워킹 시 중요한 것은, 이 실수를 어떻게 받아들이냐인것 같다.

이전에는 스스로가 누군가에게 폐를 끼치는 것 자체를 극도로 꺼려했고, 완벽하게 하려고 하다보니 오히려 더 실수가 늘어나게 되는 악순환에 빠졌었다면, 적어도 이제는 나도 실수를 한다는 것을 인정하게 되었다. 그리고 나의 실수를 인정하면 타인의 실수에도 관대해진다. 팀원들도 하고 나도 하는 실수, 결국 누구 탓을 할 시간에 우리 앞에 닥친 일을 해결해야 한다. ("사람이니까 그럴 수 있지. 자 그러면 이 문제는 어떻게 해결할까?")

결론적으로 문제의 책임소재보다는 그 문제를 어떻게 해결할 것인지에 집중했기 때문에, 팀워킹을 해치지 않는 동시에 팀의 문제 해결 능력을 기를 수 있었다. 그리고 이런 성장의 과정에서 함께 동고동락한 우리 팀장님과 팀원들에게 감사하다는 말을 전하고 싶다. 이런 개인적인 성장은 팀원들이 없었다면 절대로 이뤄내지 못했을 것이다.

profile
제가 또 기가막힌 한 줌의 트러플 소금 같은 존재그등요

2개의 댓글

comment-user-thumbnail
2021년 6월 22일

유진님 어쩌다 잘보고 갑니다 ㅎㅎ 그간 너무 수고많으셨어요~!!~ 빠른 시일내 취뽀하시길 바랍니다!! 👍🏻

1개의 답글