- Published on
Monorepo를 관리해오며
- Authors

- Name
- sulmo
모노레포를 사용한지 시간이 지나면서 중간 점검 및 정신 수양.
모노레포 도입 배경 (feat. Turborepo)
우리 팀은 6명이 디자인 시스템과 메인 앱을 동시에 개발하고 있으며, 기존에는 각 프로젝트가 독립된 저장소에 존재했다. 이와 같은 구조는 다음과 같은 불편함이 존재했다.
- 로컬 개발 환경:
symlink가 불안정하여 개발 환경 동기화가 어려웠다. - 버전 및 배포 관리: 각 프로젝트를 따로 관리하다 보니 버전 불일치나 배포 지연이 자주 발생했다.
- 도구 중복: ESLint, TS 등 설정이 레포마다 중복되어 관리 비용이 컸다.
- CI 파편화: CI가 분산되어 피드백이 느리고 복잡했다.
이러한 상황에서 우리는 turborepo와 nx와 같은 모노레포 툴들을 만나면서, 모노레포 도입을 결정했다. 단일 저장소에서 동시 개발, 원자적 커밋 보장, 공통 도구 중앙화, 그리고 CI 속도 개선을 목표로 삼아 진행했었다.
모노레포란?
현 모노레포의 시작은 내가 작업하지 않았다. 우리 사수님이 첫 도입을 진행 한 후, 도입이 완료된 형상을 보면서 확인했던 정의들은 다음과 같다.
"두 개 이상의 프로젝트 코드를 하나의 버전 관리 저장소(repository)에서 관리하는 방법을 말합니다."
"모노레포란 버전 관리 시스템에서 두 개 이상의 프로젝트 코드가 동일한 저장소에 저장되는 소프트웨어 개발 전략"
"여러 프로젝트나 라이브러리를 하나의 저장소에서 관리하는 단일 레포를 말합니다"
저 문장들을 처음 읽던 당시 "그렇지, 한 레포에 다들어있지" 하고 넘어갔다. 하지만 저 문장 뒤에 추가적으로 많은 것들이 붙어야한다는 생각이 든다. (node환경 기준)
중요한건 실제 적용하면서 흔들리지 말아야할 본질들이 있다. 거창하게 본질이라는 단어를 사용했지만 끔찍한 상태의 모노레포로 빠지지 않기위한 기본 수칙같은 것들이 있다. 엄청 큰 규모는 아니지만 N개의 앱과 M개의 패키지들을 사용하면서 정리해본 본질은 다음과 같다.
현재 개발을 하고있는 모노레포의 상태
(npx nx@latest graph를 이용)apps/
- web: 메인 콘솔 앱. Vite 기반, Vue 2.7 환경, packages에 있는 많은 프로젝트들의 의존이 있다.
- mirinae-storybook, web-storybook, unified-storybook: 스토리북 앱들. 포트가 분리된 개발 환경을 가지며, 공통 스토리북 설정 패키지에 의존한다.
packages/
- @cloudforet/mirinae: 디자인 시스템(UI 컴포넌트). Vite로 빌드되며, 타입 및 CSS 빌드 스크립트를 포함한다.
publishConfig.access=public설정이 있다. - @cloudforet/core-lib: 콘솔 공통 코어 라이브러리.
- @cloudforet/utils: 유틸리티 라이브러리.
- @cloudforet/language-pack: i18n 리소스 패키지로, 비공개로 관리된다.
- eslint-config-custom: 사내 ESLint 설정 패키지로, 루트의
.eslintrc.js가 이를 확장하여 사용한다. - postcss-config-custom, stylelint-config-custom: 스타일 빌드 및 검사를 위한 공용 설정 패키지이다.
- storybook-config-custom: 여러 스토리북 앱이 개발 의존성으로 사용하는 공용 설정 패키지이다.
- mirinae-foundation, tsconfig: 디자인 시스템의 베이스 스타일과 TypeScript 베이스 설정을 공유하는 패키지이다.
기타 루트 구성
.turbo/: Turborepo 캐시 및 쿠키 디렉토리이다..husky/: Git 훅을 관리한다..github/workflows/*: PR 빌드/테스트, 린트, 캐시 관리, 스토리북 배포 등 CI/CD 워크플로우를 정의한다.vercel.json: Vercel 배포 관련 설정 파일이다.turbo.json: 태스크 그래프, 캐시 입력/출력, 의존 관계를 정의하는 핵심 설정 파일이다.vitest.workspace.js: Vitest 멀티 워크스페이스 엔트리 포인트이다.
모노레포의 본질(지켜야할 것들)
단순히 여러 프로젝트를 한 레포에 모아두는 것만으로는 모노레포의 진정한 이점을 얻기 어렵다.
Single Source of Truth
우리의 불편함 중 하나는 같은 세팅을 여러번 반복함에 있었다. 여러 패키지에 걸쳐 공통으로 사용되는 규칙과 설정을 중앙에서 관리하여 일관성을 유지하고, 관리 비용을 절감하는 것이 핵심이다. ESLint, TypeScript, 빌드 도구 등의 공통 설정을 루트에서 정의하고 각 패키지에서 이를 상속받아 사용해야 한다.
잘 분리된 프로젝트들의 알맞은 조합
모든 코드를 한 번에 빌드하거나 테스트하는 것이 아니라, 변경이 발생한 부분과 그에 영향을 받는 패키지만을 식별하여 작업을 수행해야 한다. Turborepo와 같은 도구를 통해 개발자의 피드백 시간을 단축하고 CI/CD 비용을 절감하는 것이 모노레포의 핵심 이점이다. 작고 소중한 프로젝트라 그렇게 관리할게 없어보이지만서도 작업의 종류는 다양하고 각각의 최적화가 필요했다.
- web 콜솔 빌드
- 디자인시스템의 기능개발을 목적하고 스토리북 까지의 확인이 필요한 케이스
- (놀랍게도 갑자기 외부에서 요구한적이 있다.) npm에 디자인시스템을 올리는 케이스
- 스토리북 자체의 관리로써의 케이스
- 뭐 등등.. 이와 같이 모든 상황에 모두 빌드할 수 없다. 이해관계를 이해하고 더 쾌적한 모노레포 상태를 유지하자.
변경사항 추적
여러 패키지에 걸친 기능을 개발할 때, 각 패키지를 개별적으로 PR을 올리는 대신, 하나의 커밋과 PR로 여러 패키지의 변경을 동시에 처리해야 한다. 이는 패키지 간 버전 불일치를 방지하고 복잡한 기능 개발 시 일관성을 보장한다. 우리 프러덕트에서는 모두가 함께 개발 중이라 너무나도 좋은 효과이고 애용되는 본질이지만 정말 큰 조직에서는 어떤식으로 관리되는지 잘 모르겠다. 관리하기위한 매우 멋진 처리가 이뤄졌을 것이다.
구조적 재사용성
단순히 코드를 복사해서 붙여넣는 것을 넘어, 공통 코드와 유틸리티를 별도의 패키지로 분리하고 이를 다른 앱에서 즉시 가져다 사용해야 한다. 이는 코드 중복을 최소화하고 유지보수 효율을 높인다. N개의 프로젝트를 한 레포에서 관리하는 모노레포는 어설프게 분리되어선 안된다. 독자적으로 패키지든 앱이든 잘 이용될 수 있도록 분리해야한다.
팀 간의 소유권 및 경계 관리
코드베이스가 커질수록 여러 팀이 하나의 저장소에서 작업하게 된다. 이때 코드의 소유권과 책임 영역을 명확히 설정해야 실수로 다른 팀의 코드를 변경하거나 관리 혼란이 발생하는 것을 막을 수 있다. 팀이라고 적었지만 팀 내부네서 작업자의 담당 영역도 똑같이 생각한다.
정리
꾸준히 모노레포가 무너지지 않게끔 신경을 써줘야하지만 이로인해 얻은것들이 많다. 지금을 잘 개발 중이지만 다른 큰 팀들의 여러 고찰들을 보면서 근본적으로 개선해야할 사항들도 많이 보인다. 항상 갈아치우고 싶지만 아직까지 큰 무리가 없어 npm을 사용중인 우리 기본 패키지 관리자와 꾸준히 업데이트되는 turborepo의 편의기능들 적용.... 대부분 비슷하면서도 규모나 작업 방식이나 팀의 상태에따라 얻으려하는 것이 다를 것이다. 현재 상황에서 우리에게 어떤 이득을 가져다줄지 기대하며 재밌는 모노레포 관리를 이어가봅시다!