Monorepo 구축을 통한 5개 이상의 프로젝트 관리하기
신규 프로젝트를 진행하며 더 나은 프로젝트 설계에 대한 고민을 바탕으로 작성한 글입니다. 모노레포와 멀티레포 (opens in a new tab)에 대한 개념은 해당 링크를 통해 지식을 얻을 수 있으며, 이 글은 모노레포 도입 이유와 경험을 공유합니다.
모노레포 도입 배경
전 직장을 포함하여 프로젝트를 설계한다면 항상 멀티레포를 표준처럼 사용했습니다. 백엔드와 프론트엔드 개발자가 명확히 분리된 팀에서는 멀티레포를 이용한 방식이 팀간의 자율성을 확보하고 맥락을 명확히 분리하는 장점이 있었으나, 현재 팀은 모두가 풀스택 개발자로 구성되어 있어 프론트엔드와 백엔드를 분리한 프로젝트의 설계방식은 되려 불필요한 비용이 발생하고, 관리하는 프로젝트가 5개 이상으로 늘어남에 따라 비용이 복리처럼 크게 증가하였습니다. 프론트와 백엔드의 관리 경계를 허물고, 모두가 서비스라는 큰 맥락을 이해하며 책임을 가져갈 수 있는 방법이 필요했습니다. 따라서 모노레포 도입을 검토하게 되었습니다.
모노레포란 : 모노레포란 버전 관리 시스템에서 두 개 이상의 프로젝트 코드가 동일한 저장소에 저장되는 소프트웨어 개발 전략
우리가 겪은 문제
-
컨텍스트 스위칭 비용 : 프로젝트 간의 스위칭 비용이 높아지며, 프로젝트 간의 코드를 이해하고 넘나들며 작업하는 데 작업자들의 스트레스가 증가하였습니다.
-
관리의 어려움 : 프로젝트가 5~6개로 늘어남에 따라 프로젝트 간의 의존성 관리가 어려워지며, 코드 재사용성이 떨어지는 문제가 발생하였습니다.
-
프로젝트 초기 셋팅 오버헤드 : 프로젝트 초기 셋팅 시간이 길어지며, 프로젝트 간의 공통된 설정을 관리하는데 어려움이 있었습니다.
도구의 선택
-
Turborepo
: Zero Config를 지향하며 대규모 모노레포 프로젝트에서 오는 피로감과 부수적 툴링에 대한 부담을 줄이는 Vercel에서 인수하여 개발 및 운영하고 있는 JS/TS를 위한 모노레포 빌드 시스템입니다. Turborepo는 증분 빌드, 캐시, 병렬 빌드 등을 지원하여 높은 빌드 성능을 자랑하며, yarn이나 npm, pnpm과 같은 다양한 패키지 매니저와 함께 잘 작동하므로 프로젝트에 적용하기 용이합니다. -
Yarn workspace
: yarn workspace를 사용하여 프로젝트 간의 의존성을 관리하고, 프로젝트 간의 코드를 공유할 수 있도록 도와줍니다. 다만, 간단한 코드 공유에는 용이하나 프로젝트 관리면에서 다른 모노레포 도구에 비해 지원하는 것들이 많지 않습니다. -
Lerna
: Lerna는 저수준의 Yarn, npm 위에 있는 고수준 레이어로 볼 수 있습니다. Yarn으로 모노레포를 구성할 수는 있지만 여러 workspace의 버전 관리, 테스트, 빌드, 배포, 게시 등의 작업은 일일이 구성해야 합니다. Lerna는 이러한 작업을 최적화하는데 적합합니다.
이 밖에도 Nx
, Rush
등 다양한 모노레포 도구가 있습니다. 저희는 프로젝트의 규모와 팀의 역량을 고려하여 Turborepo
를 선택하였습니다. 가장 큰 이유는 Turborepo
가 Zero Config를 지향한다는 점이었으며, 가장 빠르게 프로젝트를 구성하고 운영할 수 있는 도구를 선택하였습니다. 그 밖에도 빌드 캐시나 병렬 처리 기법은 프로젝트의 빌드 성능을 높이는데 큰 도움이 되었습니다.
모노레포를 통한 경험
모노레포를 도입하고 수개월이 지난 지금, 프로젝트 관리에 있어서 큰 변화를 느낄 수 있었습니다.
1. 개발자의 목적 및 책임 통일
멀티레포를 지금까지 사용하면서 프론트와 백엔드를 구분하거나 프로젝트 간의 경계를 명확히 하려는 노력이 많았습니다. 그러나 모노레포를 도입하면서 개발팀이라는 조직 모두가 서비스라는 큰 맥락을 이해하고 책임을 공유하며 개발할 수 있게 되었습니다. 이로 인해 개발자들의 컨텍스트 스위칭 비용이 줄어들었으며, 프로젝트 간의 코드를 이해하고 넘나들며 작업하는 데 스트레스가 줄어들었습니다. 특히 풀스택을 지향하는 개발자들로 이루어진 팀이었기에 더 큰 효과를 체감하였습니다.
2. 프로젝트 초기 셋팅 오버헤드 감소
과거 초기 프로젝트를 셋팅하기 위해서는 프로젝트를 생성하고, 프로젝트의 설정, 의존성 관리 등에 많은 시간을 소모해야만 했습니다. 그러나 모노레포를 도입하면서 신규 프로젝트를 생성하고 공통된 설정과 파이프라인을 추가하는 것으로 초기 프로젝트 셋팅의 어려움이 줄어들었으며, 프로젝트 초기 셋팅 시간이 줄어들었습니다. 이로 인해 개발자는 초기 프로젝트 셋팅에 소비하는 시간을 줄이고, 실제 서비스 개발에 더 많은 시간을 투자할 수 있게 되었습니다.
3. 공유 패키지 관리 및 파이프라인의 편의성
모노레포를 도입하면서 프로젝트 간의 코드를 공유하고, 공통된 설정을 관리하는 것이 편리해졌습니다. 특히 공통된 타입을 공유하고, 자주 사용하는 함수를 공유하며 코드 재사용성이 증가하였습니다. 또한 파이프라인을 공통된 설정으로 관리하고, 프로젝트 간의 파이프라인을 공유하는 것이 용이해졌습니다. 특히 Turborepo의 경우 turbo.json을 통해 각 packages.json간의 pipline을 설정하여 작업관계를 정의하여 의존 관계를 직관적으로 이해시킬 수 있었습니다.
{
"$schema": "https://turborepo.org/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"]
},
"deploy": {
"dependsOn": ["build", "test", "lint"]
},
"lint": {}
}
}
결론
모노레포 도입 경험은 대체로 만족스러웠습니다. 빌드 성능도 향상되었고 개발자의 DX에도 큰 영향을 주었습니다. 앞으로 작은 프로젝트 또는 1인 프로젝트를 진행할 때, 자주 이용할 것 같습니다. 다만, 큰 팀에서 모노레포를 운용한다고 하면 명확한 컨벤션 또는 약속된 규칙을 통해 일관성을 유지하는 것은 필요할 것 같습니다. 앞으로도 개발 경험을 향상시킬 수 있는 좋은 도구가 있다면, 적극 도입해보고 시도해볼 예정입니다.