팀워크를 위한 모노레포(Monorepo) 시스템 구축, 그리고 회고
꽤 오랜 시간 동안 모노레포 이전 및 안정화 작업을 계속 진행해왔다.
그래도 이제는 상대적 주요 프로젝트들은 모두 모노레포로 이전이 완료되었고, 팀원들이 이제 모두 모노레포 내에서 작업이 가능한 수준으로 안정화를 시키게 된 것 같다.
콴다 팀 블로그에도 해당 블로그 글을 쓰긴 했지만, 내 스스로도 좋은 경험 및 큰 성장 과정이 되었던 것 같아서 내 블로그에도 기록을 남겨보고 싶어 이렇게 한번 더 작성을 해본다. (비록 내용이 거의 중복이 되겠지만..)
우리 팀은 이번 모노레포를 통해 새로운 팀워크 방향성이 생기면서, 이를 통해 향상된 팀 협업 요소들을 정리해 보려고 한다.
우선 그전에, 우리가 어떤 계기로 인해 팀 전체가 모노레포 시스템을 이용하기로 결정하게 되었는지 배경을 먼저 소개하고 싶다.
모노레포 도입 배경
기존의 우리 프론트엔드 팀의 Repository는 아마 50여개 정도는 훌쩍 넘을 것이다. 이를 멀티 레포(Multi Repo) 방식으로 관리하고 있었는데, 멀티레포는 각각에 독립적이여서 작업하고 관리하는데 수월할 수 있지만, 서비스와 팀이 커지는 과정에서 장기적으로 봤을 땐 갈수록 늘어나는 Repository를 계속해서 관리하기가 힘들어질 수 있고 리소스 낭비로 이어질 수 있다.
주로 우리 팀원이 걱정하던 요소들은 아래와 같다.
- 각 프로젝트의 코드 컨벤션이 통일하기가 어려워질 수 있음.
- 각 프로젝트 별로 사용하는 모듈 및 버전 스택이 달라질 수 있음.
- 오랫동안 건드리지 않은 프로젝트의 관리가 힘들어지며, 시간이 지날수록 해당 프로젝트의 Legacy 파악이 어려워질 수 있음.
- 팀원 별컨텍스트 공유가 서로 원활하지 않을 수 있음.
현재의 문제점을 해결해보기 위한 노력
물론 처음부터 이러한 문제를 ‘모노레포로 당장 해결해야지!’라고 생각하지는 않았고, 현재의 상태에서 최소한의 방식으로 지금의 문제점을 잘 해결해 보고자 했다.
프로젝트 Bolierplate도 만들고, 각 컨벤션을 맞추고자 eslint 관련 요소들을 모듈화 하여 각 서비스에 설치하도록 하는 등 여러 가지 시도를 해보았지만, 실질적으로 이러한 요소들도 개발자가 계속해서 신경을 쓰지 않고 사용하지 않게 되면서 무의미한 것들이 되어버리고 말았다.
또한 Repository가 독립적으로 나누어져 있다보니, 각 팀원들이 자신들이 주로 바로 보는 프로젝트 외에는 다른 프로젝트는 신경을 쓰지 않게 되는 문제도 간간히 발생했다.
서로 이러한 문제를 정기적인 컨텍스트 공유 미팅으로 해결해보려고 했으나, 이러한 미팅 역시 리소스 낭비로 이어지기 때문에 실직적인 해결방안이라 볼 수 없고, 향후 팀원과 Repo가 늘어날수록 공유해야 할 컨텍스트가 비례하게 될 것이다.
결국 모노레포를 구축하기로 결정
팀이 커지면서 많은 팀원과 많은 프로젝트가 다 같이 맞추어 나가기란 정말 쉽지 않다. 그렇기에 하나의 팀이 장기적으로 모두 align이 유지되고 협업 능률과 팀 퍼포먼스를 유지할 수 있는 환경을 만들어 가는 것은 팀과 기업 관점에서 매우 중요한 사항이기도 하다.
그래서 우리는 이러한 문제들을 해결하기 위해 모든 서비스를 하나의 레포에서 관리할 수 있도록 모노레포 시스템을 구축하기로 결정하였다.
모노레포 구축을 위해 작업했던 사항
MonoRepo Tool 리서치
생각보다 모노레포 환경을 구축할 수 있는 Tools이 꽤 많았다.
yarn workspace에 Lerna를 확장시켜서 사용하는 방식이 정말 많이 쓰이는 듯했고, 이외에는 Nx와 Rush, 그리고 내 개인적으로 가장 흥미를 느꼈던 Velcel의 Turborepo정도 있었던 것 같다.
각각의 차이는 당연히 있겠지만, 결국 큰 틀에서 모노레포 환경을 구축하여 이용하는 방식은 큰 차이가 없었다. 그래도 Turborepo에서 Lint, Test, Build 등의 각각 스테이징을 병렬적으로 처리할 수 있으며 심지어 요즘 컴파일러의 고성능으로 뜨고있는 SWC를 이용하다 보니 사실 요게 가장 관심이 많이 갔었는데, 아쉽게도 Turborepo에서는 아직 Yarn Berry(2.0)을 지원하지 않는 문제가 있었다..ㅜㅜ
그래서 우리도 결국 기존에 사용하던 것이 yarn이었으니, Yarn workspace로 환경을 구축했는데, Lerna까지는 딱히 우리 상태에서는 필요성을 느끼지 못하여 우선 Yarn workspace만으로 모노레포 환경을 충분히 구축할 수 있었다.
모노레포 CI/CD 파이프라인 구축
기존에 사용하던 Jenkins 파이프라인 코드가 우리 프론트엔드 뿐 아니라, 백엔드 팀 등 다른 팀에서도 컨벤션을 맞추어 사용하던 코드였다. 각 Repository에서 Root 경로에 Jenkins 파일을 두고 CI에서 이를 참조하는 방식이었다.
그런데 우리 팀이 모노레포 환경으로 이전하게 되면, 여러 서비스가 하나의 Repo에서 관리하게 되고 여러 개의 Jenkins 파일이 필요하게 되므로, 기존에 사용하던 공용 파이프라인을 사용할 수 없게 되었다.
결국 우리는 우리 프론트엔드 모노레포 용 CI/CD 파이프라인을 새롭게 구축하기로 결정하였다.
이 과정에서 나는 Jenkinse, Docker와 엄청나게 친해질 수 있었다...
이전에는 Docker가 어떤 녀석인지만 알고, image, Dockerizing, Docker compose, image Cache, Multi Stage 등 아무 개념도 모르는 상태였고 Dockerfile 안에 작성하는 Syntax 조차 볼 줄 모르는 상태였다.
Jenkinse 역시 Web Console에서 잘 배포되었는지만 확인할 줄 알았지, Jenkins를 어떻게 구축하고, 파이프라인이 어떻게 구현되는 것인지 전혀 모르는 상태였다.
그랬던 나에게 모노레포 CI/CD 구축 과정은 나를 지금보다 두 단계 정도 성장시킬 수 있는 작업이 되었던 것 같다.
그만큼 개념부터 계속해서 이해하려고 노력하고, 작업 중에 뭐가 또 안돼서 이슈를 맞닥뜨리면 또 삽질하며 굉장히 시간을 많이 버리는 과정을 50번 이상은 했던 것 같다.
작년 11월쯤부터 1~2개월 동안은 거의 Jenkins와 Docker 코드만 바라보고 살았다. 그것도 매일 아침부터 점심 저녁을 지나 새벽 1~2시까지 남아서.
정말 힘들었지만, 감사하게 이러한 과정이 있었기에 지금은 Jenkins와 Docker에 대한 이해도가 굉장히 많이 높아진 상태이며, 모노레포의 CI/CD를 내 손으로 직접 구축한 만큼, 파이프라인 어디서 어떻게 무엇이 돌아가는지 상세하게 파악하고 있는 상태이다.
CI/CD 파이프라인 작업은 내겐 엄청난 큰 행운이었다.
기존 서비스들을 모노레포로 이전 작업
파이프라인 구축 작업 후, 상대적 작은 서비스들 몇 개를 미리 빠르게 모노레포로 옮겨서 CI/CD 배포에 문제가 없는지, 모노레포에서 작업을 하는 것에 문제가 없는지 등등을 체크해 보았다. 작은 서비스 몇 개까지는 나름 수월했고 실제 작업내용을 Prod에 반영까지 해보았으니, 남은 프로젝트들도 수월하게 옮긴 뒤 문제없이 사용할 수 있을 것이라 생각했다.
그러나 요놈의 Yarn berry가 여러 환경의 프로젝트가 늘어나니 말썽을 일으키기 시작했다...-0-
각 서비스의 디펜던시에서 공용으로 사용되는 것은 최상단으로 Hoisting 되는데, 특정 서비스에서 런타임 시 해당 모듈을 찾을 수 없다는 문제가 발생하고, 각각의 ESLint 문제도 막 터지기 시작한다.
무엇보다.. yarn install이 굉장히 느리다..
아직 yarn berry의 pnp가 모든 타사 모듈까지 원활히 지원되지는 않는 상태라고 docs에도 적혀있기도 하고, 지금 pnp에 대해 계속해서 리서치를 해보는 중이다. 만약 pnp가 잘 적용만 된다면 느린 install 속도나 Hosting 요소를 모두 제거할 수 있을 것 같다.
린트 작업도 굉장히 힘들었다.. 애초에 지금까지 컨벤션에 맞지 않는 상태로 코드를 계속 짜 왔다 보니, 실제 컨벤션 Rule에 맞지 않는 코드들이 너무 많았는데, auto fix로 바로 수정되지 않고 아예 코드 로직 수정이 필요한 것들이 꽤 있을 때는 해당 비즈니스 로직부터 이해하고 수정한 뒤 문제가 없는지 확인까지 해야 했으니 너무나도 골치였다..
힘든 작업들이었지만 결국 끝에는 무사히 옮길 수 있었고, 이를 통해 드디어 모노레포 안의 모든 서비스의 코드들이 컨벤션을 맞출 수 있었다.
팀 내 WorkFlow 논의
모노레포로 이전하면서 브랜치 전략, PR 템플릿 등 여러가지 새롭게 논의할 것들이 많았다. 이젠 하나의 Repo에서 모든 팀원들이 작업하게 되는 만큼 Git 히스토리를 보던가 PR List를 확인하는 등의 요소들이 복잡해질 수도 있으므로, 이러한 부분들을 최대한 컨벤션을 맞춰서 빠르게 파악이 가능하도록 할 필요가 있었다.
또한 기존에 쓰던 git-flow 전략을 모노레포에서 그대로 사용하기에는 애매해서, 여러 리서치와 논의 끝에 Trunked Base Development 전략을 최대한 이용해 보기로 했다.
마냥 여러 프로젝트를 한군데로 모으기만 하면 되는 것이 아니라, 그 환경에 맞는 여러 업무 방식들도 정말 많이 연관되어 있음을 깨달을 수 있었다. 개발 환경의 변화에 따라 어떤 요소들이 연관되어있고 신경 써야 되는지 좋은 인사이트를 얻을 수 있었던 것 같다.
모노레포를 통해 개선된 점
코드 리뷰 향상
각 프로젝트의 작업 사항을 하나의 Repository에서 한눈에 확인할 수 있으니, 자연스레 다른 프로젝트의 코드를 들여다보며 코드 리뷰에 적극 참여할 수 있는 계기가 만들어지면서, 평소 내가 접하지 않던 프로젝트의 컨텍스트도 함께 쌓을 수 있는 환경이 마련된 것 같다.
혹시나 여러 프로젝트들의 PR들이 많이 올라와서 오히려 복잡해지지 않을까 걱정했지만, PR이 올라오는 즉시 모든 멤버들이 적극적으로 코드 리뷰를 하게 됨으로써 피드백이 바로 반영되며 Merge가 되기 때문에, 생각한 것만큼 복잡한 PR 리스트가 쌓이지 않을 뿐 아니라, 더욱 빠른 피드백과 리뷰 반영 시스템을 갖출 수 있게 되었음을 몸소 느낄 수 있었다.
모든 웹 프로젝트의 최신화 상태 유지
기존 멀티레포 환경에서 수많은 프로젝트로 인해 오래 건드리지 않던 프로젝트가 Legacy 화가 되어 왔으나, 이제는 모노레포를 통해 한층 더 신경 쓸 수 있는 환경으로 개선시킬 수 있게 된 것 같다.
오랫동안 작업 사항이 없어 건드리지 않다가 최근에 새롭게 작업할 사항이 생겼을 때, 현재와는 너무나 달라진 Legacy 코드로 인해 실제 작업량보다 최신화 상태로 개선시키기 위한 작업으로 인해 예상치 못한 리소스가 낭비되기 일쑤였는데, 이제는 모노레포 환경을 통해 오랫동안 건드리지 않는 프로젝트도 주기적으로 함께 관리할 수 있는 환경이 되면서 모든 웹 프로젝트를 최신화 상태로 보존할 수 있게 되었다.
팀 컨벤션 통일 및 업무 능률 향상
각 프로젝트 별로 align이 되지 않던 eslint와 prettier의 환경이 모노레포로 통일되면서 드디어 모든 팀원이 동일한 코드의 컨벤션을 가져갈 수 있게 되었으며, 더 이상 린트 환경 및 설정에 대해 신경 쓰지 않게 됨으로써 실제 코드 작업에 더욱 집중할 수 있게 된 것 같다.
뿐만 아니라, 각기 독립되어 있는 프로젝트의 컨벤션을 맞추고자 사용했던 Boilerplate, eslint-config와 같은 모듈을 더 이상 사용하지 않아도 되며, 추가로 프로젝트에서 필요한 설정 및 환경이 있으면 팀이 다 함께 의논한 뒤 반영이 되는 만큼 팀 협업을 좌우하는 컨벤션이 신중이 반영될 수 있게 되었다.
글을 마치며
사실 아직 모노레포의 개선점이 너무나도 많다. 서비스가 많고 이제는 하나의 레포가 무거운 만큼, 어떻게 하면 팀원들이 모노레포를 단일 레포처럼 가벼운 느낌으로 사용할 수 있을지도 개선이 필요하고, 무엇보다 속도 개선이 너무나도 중요하다.
지금도 이러한 문제를 팀원들과 어떻게 해결할지 많은 방법을 찾아보고 실험 중이며, 그 과정에서도 새롭게 알게 되는 기능과 기술들도 많이 알게 되는 것 같다.
모노레포 작업 이전에는 사실 '굳이 모노레포를 왜 해야 하지? 꼭 필요할까? 지금도 나쁘지 않은 것 같은데?'라는 생각이 꽤 있었는데,
팀이 소규모일 때에는 프로젝트 수가 적고 팀원이 적은 만큼 멀티레포 방식에도 충분히 팀의 align이 가능하고 팀워크 유지가 가능할 수 있지만, 팀이 커지면서 팀원과 프로젝트 수가 늘어날수록, 모든 팀원들이 다 함께 맞춰 나가기가 어렵고, 우리 콴다 역시 빠르게 성장하는 만큼 멀티레포의 방식은 모든 팀원이 맞춰가기가 굉장히 어려운 환경이었다.
지금 생각하면, 현재 빠르게 커져가는 우리 팀의 입장에서 향후에 맞이할 프로젝트 관리, 코드 관리, 팀워크 관리 등에 대해 발생할 문제들에 대해 미리 대비하기 위한 준비라는 생각이 든다.
너무나 좋은 과정이었고, 앞으로도 이와 같이 다양하고 좋은 경험을 계속해서 많이 쌓아갈 수 있으면 좋겠다.