[Retrievo] 당신의 프로젝트를 관리해드립니다(feat. RPQ)

Si Choi·2020년 12월 25일
0

Retrievo란?

1. 프로덕트 소개

Retrievo는 팀 프로젝트를 팀장과 팀원들이 관리할 수 있도록 돕는 협업 툴입니다. 전체적인 컨셉은 애자일 기법을 참조하였으나, 모든 종류의 프로젝트에서 활용할 수 있게 설계 되었습니다.

Retrievo라는 제품명은, 저의 팀원들이 제가 아무도 상상치 못한 에러들을 물어오고 일을 가져와 준다고 하여 리트리버 라는 별명을 주셨는데, 이 별명을 모티브 삼아 "귀여운(?) 리트리버가 할 일을 물어와준다"라는 컨셉으로 제품의 명칭을 지었습니다.

2. 주요 페이지

여기서 잠깐!
아래의 페이지들의 구현과 백엔드 작업은 저 혼자서 개발한 게 아니며, 저희 팀원들이 함께 개발한 부분들을 포함하고 있습니다.

2-1) 랜딩 페이지

UIUX적인 측면에서, 사용자들이 저희 서비스에 들어오게 되면 가장 보는 페이지가 랜딩 페이지이기 때문에, 저희 서비스에서 제공하는 기능들을 GIF와 SVG 파일들을 이용해서 소개했습니다(이 페이지는 다른 팀원분들이 작성해주셨습니다).

2-2) 프로젝트 생성

먼저 가입을 하고 가장 나오는 페이지는 프로젝트 생성 페이지입니다. 가입된 사용자의 경우에는 대시보드가 가장 먼저 나오며, 프로젝트 생성이라는 버튼을 클릭하여 프로젝트를 생성할 수 있습니다.

2-3) 대시보드

대시보드는 현재 나에게 주어진 업무들(My Task)와 다른 팀원들이 진행 중인 작업이나 액션들(Action Stream)을 보여주며, 그 밑에는 현재 프로젝트의 진행 상황을 나타내는 차트와 그래프들이 있습니다.

2-4) 스프린트

스프린트의 경우 현재 진행중인 스프린트(헤더가 Teal인 경우)와 앞으로 진행할 스프린트(헤더가 하얀색인 경우)로 나눠져 있으며, 각각의 Sprint는 Drag and Drop이 가능합니다.

각각의 Sprint 안에는 해당 스프린트의 Task가 나열되어 있으며, 해당 태스크들도 Drag and Drop 기능이 지원됩니다. 이 밖에도 이 페이지에서는 Sprint와 Task에 대한 CRUD(생성, 시현, 수정, 삭제) 등의 기능이 제공되고 있습니다.

2-5) 태스크 바

생성된 Task의 정보를 업데이트 및 삭제할 수 있습니다. 한 번씩 정보가 수정될 때마다 Toast가 화면 하단에 생겨서, 유저가 정보 수정/삭제에 성공했는 지 실패했는 지를 알려줍니다(GIF 화면이 다 커버하지 못 해서 화면 상에서는 안 나오네요 TT).

2-6) 칸반 보드

칸반 보드는 현재 진행 중인 스프린트를 각 보드별로 나눠서(Todo, In Progress, Done) Drag and Drop을 할 수 있는 페이지입니다. 화면 상에서는 Dummy Data를 임의로 생성해서 집어넣었기 때문에 소개한 내용과 살짝 다를 수 있으나, 스프린트가 처음 생성되고 시작될 때는 백단에서 Todo, In Progress, Done 보드가 자동으로 생성되게 설계 되었습니다.

2-7) 프로젝트 세팅

(갑분 Giphy.... 이제서야 널 보게 되다니... 귀찮으니깐 크리스마스 이브 저녁이니깐 넘어가마..)

프로젝트 세팅 페이지에서는 프로젝트의 명칭을 수정하거나 해당 프로젝트의 삭제가 가능하며, 현재 이 프로젝트에 참여한 팀원들의 정보를 확인할 수 있고, 팀원의 Access Permission을 수정할 수 있습니다.

세팅 페이지에서 정보를 수정하려면 유저는 Role이 Admin 상태여야 하며, 그렇지 못 한 경우 유저는 위 내용들을 확인할 수는 있지만, 수정 권한이 없어서 수정을 못 합니다(화면 상의 유저는 Admin 권한이 있는 유저입니다).

3. 기술 스택

저희 팀은 백엔드와 프론트엔드로 나눠서 작업하지 않고, 각자가 업무를 나눠서 풀스택 개발을 진행했습니다.

주요 기술 스택은 다음과 같습니다.

3-1) TypeScript

처음 TypeScript와 관련해서 Reddit 및 여러 개인 블로거들의 리뷰를 참조했을 때 공통된 의견은 "Cannot read null" 혹은 "Cannot read undefined" 등의 에러가 사라짐으로서 가장 중요한 문제들을 공략할 수 있었다는 점입니다.

TypeScript를 처음 접하게 된 저로서는 AnyScript에 대한 유혹이 컸지만, EsLint(AirBnb Style)와 TypeScript Config 세팅에서 Any를 못 쓰게 막아놨습니다.

그러다 보니 TypeScript는 항상 저에게 화를 냈는데... 그 문제들을 마주칠 때마다 저도 TypeScript에게 화가 났지만 자잘한 문제들을 미연에 방지할 수 있었고, 어느 순간부터는 미리 try&catch문 혹은 if문으로 null check를 하고 에러 핸들링을 하는 제 자신을 보고 나름 뿌듯해졌던 것 같습니다.

이 뿐만이 아니라 Type을 미리 지정을 해놓다 보니 다른 팀원이 작성한 코드에 대한 이해도도 높아서 협업의 관점에서도 많은 도움이 되었습니다.

3-2) Husky & Circle CI

Husky의 경우 저의 팀원이 Shell Script로 작성 해주셨는데, git commit을 할 때 commit message에 자동으로 Jira 기준 해당 태스크의 고유 번호가 입력 되도록 설정 되었고, 잘못된 Git Workflow의 발생 혹은 eslint 에러가 있는 브랜치의 커밋이 못 되도록 작성되었습니다.

그 결과 잘못된 Git Commit으로 발생하는 자잘한 문제들을 미연에 방지할 수 있었고, 저희도 많은 기회 비용을 절감할 수 있었습니다.

Circle CI의 경우에는 저와 제 팀원이 AWS EC2와 S3 버켓에 연결 하였는데, GitHub의 Staging 혹은 Master Branch에서 Merge Request가 발생할 때 작동하도록 설계 되었습니다

Circle CI를 AWS와 연계할 때 CircleCI SSH 키를 이용하는 방식을 활용했습니다.

** 관련 블로그 링크

3-3) GraphQL & TypeGraphQL & TypeORM

제 경험과 지식의 한도 내에서 판단을 하자면, 타입스크립트를 쓸 때 GraphQL과 가장 호환성이 좋은 건 TypeGraphQL이었습니다.

GraphQL을 작성할 때 Schema-First 방식과 Code-First 방식이 존재합니다.

간단히 설명 하자면, Schema First 방식은 먼저 query & resolver 등을 먼저 정의하고 그 다음에 resolver를 작성하는 방식이고, Code-First 혹은 Resolver-first 방식은 Resolver를 먼저 작성한 후, 쿼리를 실행시킬 때 schema가 자동 생성되는 방식입니다.

** 참조 링크

TypeGraphQL의 경우에는 Code-First 방식으로 작성 되는데, 저희 팀에서는 Schema-First 디자인을 채용하지 않은 이유가 1) Schema First 디자인에서 Query&Mutation에 대해서 정의를 내릴 때의 Type과 TypeScript의 Type을 호환시키는 과정이 복잡하고 2) TypeGraphQL이 TypeScript와 호환성이 좋고 관련 자료나 커뮤니티가 많았다는 점 입니다.

처음 TypeGraphQL과 TypeORM을 썼을 때는 각 프레임워크의 Syntax를 혼동하여 이해를 하는 데 있어 많은 어려움이 있었습니다. 특히 유명 유튜버들의 튜토리얼을 보더라도 프로젝트 첫 1주 동안은 과연 프로젝트를 진행할 수 있을 지 의문이 들 정도로 힘들었습니다...

이 부분만 잡혔다라고 한다면 그 다음부터는 CRUD 과정이기 때문에 나름 적응을 할 수 있었던 것 같습니다.

또한, GraphQL이 내가 원하는 정보를 불러올 수 있다는 장점이 있지만, 반대로 어떤 Entity를 기준으로 각각의 Mutation이나 Query가 정보들을 Nest해서 불러올지... 고민이 많았고, 각각의 Entity의 관계를 형성할 때 처음에는 최대한 심플하게 작성을 했다가, 데이터가 너무 Nesting이 되다보니 각각의 Entity를 직접 이어준 경우도 있었습니다...

역시 처음 GraphQL을 접하다 보니 어떤 방식이 올바른 지 고민이 많았던 것 같고, 결국 저희 팀은 퍼포먼스가 가장 효율적이게 되려면 최대한 Nesting은 3 뎁스 이상을 넘기지 말기로 합의하고 작업을 진행했습니다.

3-4) Apollo 프레임워크

처음 Apollo 프레임워크를 접했을 때 가장 어려웠던 부분이 State Management였는데... 알고보니 Apollo는 정말... 훌륭한 프레임워크였습니다...

Apollo 프레임워크가 Remote State을 관리하는 방식을 보면, 일단 Query나 Mutation이 한 번 실행이 되면, 그에 대한 데이터는 캐시 메모리에 저장 되고, 데이터에 대한 수정이 발생될 때만 Refetch Query 혹은 WriteQuery, Modify 등을 이용하여 화면의 UI를 Re-render 할 수 있었습니다.

저는 처음 Apollo GraphQL 를 접할 때, Cache 데이터와 관련해서 많이 헷갈렸었는데..

핵심은 Cache 데이터는 Mutation이 발생한다고 하여 바로 업데이트가 되는 것이 아니며(리랜더링 될 때는 Refetch 설정이 되어 있으면 수정됨), UI에서 보이는 데이터들을 업데이트 하려면 updateQuery나 Modify 등을 써서 Cached Collection을 수정하고, 자동으로 Rerendering이 Trigger되게 작성을 해야 한다는 점입니다(updateQuery, modify가 실행되면 Rerendering이 자동으로 실행 됨)

** 참조 링크

또한, 한 가지 명심할 점은 Apollo cache와 React의 State을 혼동하면 안 된다는 점이며, 만약에 cache 데이터와 React의 state을 연동시켰을 경우에는, cache 데이터만 수정하지 않고 동시에 react의 state도 변동이 되도록 설정을 해야한다는 점이었습니다(저는 useEffect를 썼습니다.. 물론 이런 경우가 많으면 안 되지만, Beautiful DnD를 이용할 때는 이런 방식을 채용했습니다. 자세한 사정은 바로 밑에서...).

3-5) 기타(React Dates, Beautiful DnD, StoryBook, Chakra)

TaskBar에 있는 달력은 AirBnb에서 개발한 React Dates을 썼는데, 그 전에 미리 Moment.js와 CSS 애니메이션만으로 캘린더를 구현했던 저로서는 이 라이브러리가 얼마나 위대한 지 몸소 체험할 수 있었던 기회였습니다.

특히, React Dates의 경우는 AirBnb가 문서화도 너무 잘 해놔서 하라는 대로 하면 됐고, 캘린더를 Customize를 하는 과정에서도 Google Chrome Inspector에서 각 Element의 className을 참조하면서 쉽게 수정할 수 있었습니다.

또한, Drag and Drop 같은 경우에는 Beautiful DnD를 썼었는데, 제 개인적인 생각으로는 React DnD보다 더 편리했던 것 같습니다.

** 참조 링크

한 가지 아쉬웠던 점이라고 한다면, Beautiful DnD의 경우 state으로 Row 위치가 관리되고, 저희가 GraphQL Mutation 단계에서도 Row 정보를 입력하고 UI가 참조하도록 설계를 하였는데, 그렇다 보니 Drop을 했음에도 불구하고 Target이 Drop 된 이후 원래 자리로 이동한 다음 다시 Destination으로 이동하는 현상이 있었습니다.

그래서 해결책은 State과 DB 상의 데이터를 분리해서 관리 하기로 했는데, 아직 더 좋은 해결책을 찾지 못 하고 있습니다 TT(아시는 분은 댓글 부탁드립니다 ^^)...

또한, 저희 팀은 첫 프로젝트에서도 StoryBook을 썼는데, StoryBook은 역시나 협업할 때 1) 서로의 코드를 확인할 수 있고 2) Actions & Control을 통해서 UI를 확인할 수 있다는 점에서 너무 좋았던 것 같습니다.

마지막으로 Chakra의 경우 너무나 좋은 Material들을 제공해줘서 사용하기는 편리했지만, Documentation이 디테일하지 않은 경우가 있고 Chakra가 비교적 최근에 나왔다 보니 관련 자료를 찾는데도 어려움이 있어서 아쉬웠습니다...

다만, 그 만큼 좋은 Material들을 제공해줘서 MVP 단계의 프로덕트는 Chakra를 써도 좋겠다는 생각이 들었습니다.

4. 마지막으로...

4주 프로젝트를 진행하면서 TypeScript, GraphQL, Apollo 프레임워크를 처음 접했는데, 결국 개발이란게 내가 작동 원리를 이해한다면 Syntax는 중요하지 않다는 점을 느끼게 된 계기였습니다...

물론, 저의 팀원들이 아니였다면 혼자서는 결코 진행하지 못 했을 거라는 생각이 듭니다.

제 능력 부족으로 "잠못이룬" 4주였지만 언제나 함께 할 때 웃음을 주셨던 제 팀원들 덕분에 너무 즐겁게 개발을 할 수 있었습니다. 직접 말 하겠지만, 이 글을 읽고 계신다면 다시 한 번 감사드린다는 말 전해드립니다.

마지막으로, 물고기를 잡는 방법(?)을 몸소 가르쳐주신 코드스테이츠와, 물고기를 잡을 수 있도록 도와준 저의 참스승님 Ben Awad님께도 감사를(TypeScript, Apollo, GraphQL을 공부하고 싶은 분들은 유튜브에서 Ben Awad를 검색해보세요)...

Adios 2020~!

profile
함께 성장하는 개발자가 되겠습니다!

2개의 댓글

comment-user-thumbnail
2021년 6월 29일

안녕하세요 :) 혹시 차트 라이브러리는 어디 라이브러리를 사용했는지 여쭤도 괜찮을까요?

1개의 답글