모노레포를 사용한 경험이 있지만, 정작 모노레포를 활용하는 다양한 경험은 부족하다는 생각이 들었다. 내가 경험했던 모노레포는 그냥 구조만 모노레포였던게 아닐까 하는 의심과 함께 모노레포를 사용하는 이유와 모노레포를 위한 패키지 매니저를 비교해본다.
요즘 모노레포가 유행이 되면서 많이 사용하는것 같은데 정확히 파악하고 쓰면 더 유용할 것이디.
모노레포란
모노레포(mono repo)의 정의는 간단하다. 여러개의 레포지토리로 만들었을 프로젝트나 패키지들을 하나의 레포지토리에 담는 것이다. 모노레포의 반대는 멀티 레포(multi repo)이다.
모노레포를 쓰는 이유
모노레포를 쓰면 좋은 이유는 모노레포가 도입되기 전의 문제점으로부터 알 수 있다.
모노레포 도입 전의 문제점들
1. 중복 코드가 너무 많음
- 여러 프로젝트에 같은 유틸 함수, 디자인 컴포넌트들이 복붙돼 있음
- 변경하려면 모든 레포에 가서 수정해야 하는 번거로움.
2. 버전 지옥
- 예: UI 라이브러리 버전이 각 프로젝트마다 달라서 버전 충돌이 자주 발생함
- 어느 프로젝트가 최신인지 헷갈리기 쉬움
3. CI/CD 관리가 개별적이라 느림
- 프로젝트가 여러 개면, 각각 CI 설정이 다 따로 되어 있어서 관리 포인트도 늘어남
4. 신규 프로젝트 온보딩이 복잡함
- 팀에 새로 들어온 사람은 어떤 레포가 어디에 있는지부터 헷갈림
여러 문제 중에서도 가장 핵심적인 것은 코드 중복의 문제이다. 단순히 중복된 것 자체가 문제가 아니라, 제때 변경되지 않기가 쉽기 때문이다. 레포1에서 변경한 코드가 레포2에도 있다면 거기에서도 변경해야 한다. 하지면 이걸 매번 파악하고 고치는건 쉽지 않다. 제때 변경되지 않은 상태로 넘어간 것은 시간이 한참 지나서야 발견되고, 그때는 문제가 복잡해져 있는 경우가 많다.
이 문제를 해결하기 위해서 공통으로 사용되는 util이나 ui system같은것을 별도 패키지로 만들어 배포하기도 하는데 이경우에도 하나의 레포를 더 관리하게 되는 셈이고, 버전 관리가 제대로 안되면 역시 문제가 된다. 하나의 패키지로 배포하기가 애매한 종류의 코드도 많다.
이런 문제를 해결하기 위해 하나의 레포에 모든 패키지를 넣고, 코드를 공유하게 되면서 중복 코드에 대한 걱정을 하지 않아도 되고 개발의 생산성도 훨씬 더 좋아졌다.
모노레포의 문제
모노레포에 기존 레포의 문제를 해결하지만, 모노레포에는 새로운 문제들이 있다.
- 초기 설정이 너무 복잡함.
- 의존성이 꼬이는 경우
- 빌드 시간이 오래걸림(작은 변경이 모든 패키지의 전체 빌드를 유발)
- CI/CD 설정이 복잡함
- PR 충돌: 올라오는 PR 자체가 많아지고, 한 사람이 신경써야 할 범위도 많아짐
특히 ESLint / Prettier, Typesciprt, Storybook, jest 등의 개발환경 셋업에서 패키지 root가 어딘지 헷갈리거나 자동으로 찾지 못해서 에러가 나거나 수동으로 path를 설정해야 하는 경우가 종종 생긴다.
새로운 모노레포 툴이나 신기능이 계속 나오는데, 기존의 문제를 해결하면서도 새로 발생하는 위와 같은 문제들을 해결하는 방향으로 발전하고 있다.
- packages/config 처럼 하나의 패키지에 공통 config를 담는 방식
- 의존성 그래프를 시각화 해주고, 순환 참조를 감지해서 의존성 꼬임 문제 해결.
project graph
기반으로 영향을 받는 패키지만 리빌드(Turborepo, Nx)
모노레포를 위한 도구들
모노레포를 위한 도구는 Lerna, Nx, Turborepo, yarn workspaces, pnpm workspaces, Chnagesets 등이 있다. 이들이 하는 역할이 다 같지가 않다. 그래서 서로가 대체하는 관계가 아니라 Nx + Lerna, Turborepo + Lerna 처럼 조합해서 많이 쓰인다.
이들 모노레포 도구는 보통 두 가지 계층으로 나뉜다.
-
패키지 설치와 의존성 연결을 담당하는 기반 시스템
- 예: Yarn Workspaces, PNPM Workspaces
-
빌드 / 테스트 / 배포 자동화를 담당하는 상위 시스템
- 예: Nx, Turborepo, Lerna, Changesets
Yarn Workspace / PNPM WorkSpace
먼저 yarn worksapce는 Facebook 내부에서 여러 패키지를 효율적으로 관리하려고 도입한 도구이다.
node_modules
를 루트에서 통합 관리- 각 패키지를 symlink로 연결해줌
.workspace
설정만으로 구조 간단
나중에 pnpm workspace라는것도 나왔는데, pnpm의 장점을 따라 설치 속도 빠르고 디스크 효율이 높은게 장점이다.
현대 모노레포는 기본적으로 workspace를 전제로 사용한다고 보면된다.
이후는 **빌드 / 테스트 / 배포 자동화를 담당하는 라이브러리 **
Lerna
- 전통적인 모노레포 툴
- 여러 패키지를 관리하면서 수동으로
npm publish
하는게 너무 번거롭고 위험해서 **여러 패키지를 한 저장소에서 버전 관리하고 배포 자동화하려고 등장. - Git diff 기반으로 변경된 패키지 감지해서 버전을 올려줌.
- 단독으로는잘 안씀
Nx
- 구글의 Angular 팀이 내부에서 쓰던 도구를 오픈소스화하면서 등장.
- 패키지 간의 의존성 그래프를 자동으로 분석
- 변경된 부분만 정확하게 빌드
- TypeScript AST 기반 의존성 분석으로 코드 레벨에서 어떤 모듈이 다른 모듈을 참조하는지 파악`
Turborepo
- Verce에서 만든 모노레포 빌드 도구로, Lerna는 너무 낡았고, Nx는 너무 복잡하니까 만든 쉽고 빠른 실용적인 툴
- Vercel 및 Next.js와의 연동이 쉬움.
Changesets
- 기존 모노레포에서 어떤 패키지가 변경됐는지 알기가 어려워서, git 기반으로 변경을 명시하고 배포를 자동화하는 툴 등장.
- GitHub Actions랑 잘 연동됨
마무리
모노레포에 대한 이해는 어느정도 되었지만, 직접 써보기 전에는 각 도구들의 특징을 글만으로는 알기 좀 어려웠다. 이번에 이해한 것을 바탕으로 새로운 도구를 정할때 도움이 많이 될 것 같다.