이번에 크위치를 모노레포로 전환했다. 그 이유와 어떻게 사용하고 있는 지, 겪었던 어려움 등을 얘기해보려고 한다.
크위치 프로젝트는 이제 하나의 레포로 관리됩니다.
https://github.com/team-kwitch/kwitch
A monorepo is a single repository containing multiple distinct projects, with well-defined relationships.
모노레포(monorepo)는 여러 프로젝트를 포함하는 하나의 레포지토리를 말한다.
하지만 단순히 코드를 모아 놓는다고 해서 모노레포라고 정의하진 않고, 잘 정의된 관계가 있어야 한다.
흔히 스프링에서 사용하는 멀티 모듈과 비슷하다.
이름이 비슷하지만 microservices와 반대되는 monolith(ic)과는 다른 개념이다.
https://blog.nrwl.io/misconceptions-about-monorepos-monorepo-monolith-df1250d4b03c
기존에 사용하던 kwitch-server, kwitch-web으로 분리하는 방식은 멀티레포(또는 polyrepo) 불리며, kwitch로 통합한 방식이 바로 모노레포이다.
새로 시작해야 하는 프로젝트에 대해서 코드 공유를 하려면 쉽지 않다.
라이브러리를 만들고, 여러 툴과 CI 환경을 만들고 라이브러리를 관리하고, 배포해서 필요한 프로젝트가 의존하게 만들고, 버전을 관리하는 등등 여러가지 일을 해야한다.
모노레포에선 이런 과정이 한 레포지토리 내에서 관리되어, 기존의 CI 환경을 그대로 사용하고, 모든 프로젝트의 라이브러리가 최신화 되어있으므로 관리 또한 쉽다.
위와 같이 코드 공유가 복잡하기 때문에, 공통되는 서비스나 컴포넌트에 대해 공유 라이브러리를 일일히 만들려고 하지 않는다. 따라서 각 프로젝트에 중복되는 코드들은 계속 쓰일 수 밖에 없고 이는 시간 낭비에다 유지 관점에서 큰 숙제가 된다.
**모노레포에선 공유되는 코드들을 한 레포지토리에 관리하고, 템플릿 또한 같이 관리할 수 있으므로 이런 중복을 모두 생략할 수 있다.
어떤 기능을 개발하기로 했을 때, 프로젝트가 프론트, 백 영역으로 나누어져 있다고 했을 때, 모노레포에선 한 개의 커밋이 이 모든걸 수정할 수 있도록 하므로, 모든 커밋에 대해 실행이 가능하다는 안정성이 보장된다.
제 3자 라이브러리가 여러 프로젝트에서 다른 버전으로 사용되고 있다면, 각 프로젝트마다 해당 라이브러리가 충돌을 일으키는지 확인해야 한다.
모노레포에선 한 개의 의존성으로 모든 프로젝트에서 사용할 수 있기 때문에 버전 별로 관리하는 불편함이 없다.
추가적인 이득이 많지만 이정도로 마치려고 한다. 아직은 생소한 개념이라 프로젝트에 적용하기에 어려움이 있을 수 있지만 알게된다면 이전의 개발방식을 뒤집어 놓을 수 있는 개념이다.
Monorepo Explained에서 더 자세히 확인이 가능하다.
크위치는 기존에 멀티레포로 관리되고 있었다. 위의 많은 장점들이 있지만 그 모든 것을 알고있진 않았고 크게 두가지 이유로 모노레포로 전환하게 되었다. 그 이유는 다음과 같다.
우리의 프로젝트는 아직 도메인이 성숙해지지 않았기 때문에, 인터페이스를 변경할 일이 잦았다. 물론 잘못된 설계에서 비롯된 것도 있지만, 새로 추가되는 도메인도 많다.
하지만 이를 적용하기 위해선 프론트, 백 양 측에서 수정이 있었어야 했다. 어떤 필드를 추가하기 위해서도 백을 먼저 최신화 했다고 하면 프론트에 들려 똑같이 최신화를 했어야 했다.
그래서 들게 된 생각이 한 라이브러리에서 도메인을 관리하고 이를 프론트, 백에서 같이 참조하여 동기화되었으면 좋겠다는 생각이 들었다.
이건 우리 프로젝트의 구조이다. pnpm과 turborepo를 사용하여 모노레포를 구성했다.
apps
에 위치하고 있다.packages
에 위치하고 있다.
NX(모노레포 툴)을 사용하고 있지는 않지만 그래프를 그리는 기능으로 참조 현황을 확인해보았다. 도메인은 데이터베이스, api, web 모두 영향을 끼치고 있고, 이는 전 영역에서 도메인이 획일화되어 있음을 알 수 있다.
크위치는 현재 홀로 개발 중이지만 진행사항을 확인하기 위해 개발에 적절한 절차가 있다.
Github에서 이슈를 발행한다. (라벨을 이용하여 기능 추가인지 버그인지 구별한다.)
시작 전에 Github Project를 이용하여 프로젝트의 우선 순위나 난이도를 판단한다.
기능 개발 이후 메인 브랜치로 PR을 만들어 통합한다. (아직 CI/CD를 구현하지 못했는데, 추가할 예정이다.)
문제는 버튼 하나를 만든다고 해도 프론트, 백 영역으로 나누어져 있기 때문에 위 과정을 두번 반복해야했다. 또한 백에서 이슈가 끝나도 프론트의 이슈가 남아있어 배포를 하지 못하고 기다려야 하는 상황이였다.
하지만 이젠 하나의 이슈 탭으로 관리가 가능하고, 해당 이슈가 끝나면 서비스는 해당 기능에 대해 완성되어 있다.
좋은 점이 너무 많은 모노레포지만 모노레포가 널리 알려진건 얼마 안돼서 참고할 수 있는 레퍼런스가 많이 없었다. 거의 공식 문서에 의존하며 개발한 듯 하다.
처음에는 pnpm만을 사용해서 빌드 후 배포를 진행하려고 했는데 여러가지 어려움이 있었다.
.js
확장자를 이용해서 import 하는 등 (Pure ESM package) 기존에 프레임워크에서 단순하게 빌드하고 실행 가능했던 과정들에 비해 어려웠다.Vercel에서 만든 monorepo를 위한 툴이다.
모노레포는 각 workspace마다 고유의 테스트, 린트, 빌드 과정이 있기 때문에 한 모노레포에서 실행해야할 명령어가 무지 많아진다.
Turborepo는 이런 스케일링 문제를 해결해준다. cache가 이 모든 결과를 저장해서 같은 작업을 두번 이상 하지 않게 해준다.
또한 위에서 얘기했던 의존성 문제를 깔끔하게 해결해줬다. Turborepo의 공식 문서가 리포지토리를 제작하는 것부터 각 프레임워크를 어떻게 다뤄야 하는지, 어떻게 라이브러리를 만들고, 어떻게 CI를 해야하는지 등등 자세하게 설명해준다.
특히 여러가지 example들이 제공되고 있어 코드로 확인하기도 너무 좋았다.
개인적으로 프로젝트가 하나인 레포를 시작할때도 이런 툴을 사용해서 시작하는 것이 좋을 것 같다. 물론 모노레포를 적용한다는 의미는 없지만 정말 많은 것들을 편하게 시작하도록 도와준다. (기존의 레포를 나중에 전환하는 것보다 훨씬 편하게 적용할 수 있다.)
추가적으로 Nx라는 비슷한 툴이 있는데, 어느쪽이든 환경이 잘 구성되어있어 무엇을 선택해도 괜찮을 것 같다. Nx 측에서 작성한 비교 글이 있는데 확인해보면 좋을 것 같다.
개인적으로 얼리어답터 성격이라 어려움이 있었지만 굉장히 재밌었는데, 적용해보니 프로젝트가 훨씬 깔끔해진 것 같아 너무 좋았다.
물론 크위치에서는 프론트, 백을 모든 레포에 같이 두었는데 이는 혼자 개발하는 환경이다 보니 이렇게 채택했지만 팀의 상황에 따라 프론트, 백을 따로 사용한다던지, 멀티레포가 더 나은 선택이라던지 자유롭게 정할 수 있을 것 같다.
만약 모노레포의 힘을 보고싶다면 이 영상을 추천한다
Nx를 이용해서 한 어플리케이션을 만드는데, 설명하면서 만드는데도 순식간에 만들어버리면서 위에서 얘기한 장점들을 잘 보여준다. Nx측에서 이런 영상들을 많이 만들어놓아서 이해하기 정말 편했다. (Turborepo를 사용하긴 했지만 개념 자체는 비슷하기 때문이다.)