drrr은 백오피스와 실제 서비스를 관리하는 두 개의 프로젝트를 운영해야 했습니다. 이 과정에서 프로젝트 관리의 복잡성과 중복 코드 및 컴포넌트 관리의 비효율성이 예상되었습니다.
이 문제를 해결하기 위해 토스 기술 블로그에서 소개된 Monorepo 구조에 주목하게 되었고, 이를 drrr 프로젝트에 도입하기로 결정했습니다. Monorepo를 적용함으로써 두 프로젝트가 공통으로 사용하는 컴포넌트를 하나의 저장소에서 통합적으로 관리할 수 있게 되었으며, 이를 통해 중복 작업을 줄이고 효율적인 개발 환경을 구축할 수 있었습니다.
각 프로젝트나 서비스가 별도의 저장소에 관리되며, 프로젝트마다 독립적인 버전 관리와 개발 환경을 가집니다.
장점
단점
여러 개의 프로젝트 또는 패키지를 단일 저장소에서 관리하는 방식을 의미합니다. 이 방식은 하나의 저장소에서 서로 관련된 프로젝트들을 효율적으로 관리하고 통합할 수 있도록 설계되었습니다.
장점
1. 코드 공유와 재사용성 향상
- 모든 패키지가 동일한 저장소에 있으므로, 코드 공유와 모듈화가 용이합니다.
- 공통 코드 업데이트가 바로 다른 패키지에 반영됩니다.
2. 통합된 버전 관리
- 단일 버전 관리 시스템으로 모든 프로젝트를 관리할 수 있어 변경 사항 추적이 간편합니다.
3. 일관된 개발 환경
- 저장소 전체에서 동일한 개발 환경과 도구를 설정할 수 있습니다.
4. CI/CD 관리 단순화
- 단일 파이프라인에서 여러 프로젝트를 관리할 수 있습니다.
단점
1. 복잡성 증가
- 저장소 크기가 커지고, 빌드 및 테스트 시간이 길어질 수 있습니다.
2. 권한 관리의 어려움
- 저장소 전체에 대해 접근을 부여하거나 제한해야 하므로, 특정 프로젝트에만 접근을 허용하기 어렵습니다.
3. 초기 설정 부담
- 효율적인 관리를 위해 추가 도구 및 설정이 필요할 수 있습니다.
lerna는 Monorepo 관리의 초기 도구 중 하나로, 다수의 Javascript/Typescrirpt 패키지를 효율적으로 관리할 수 있도록 설계되었습니다.
주요 특징
주요 특징
주요 특징
Graph Analysis: 프로젝트 간 의존성을 시각화하고, 필요한 부분만 빌드/테스트.
Plugin Ecosystem: Angular, React, Node.js 등을 위한 다양한 플러그인 제공.
Custom Executors: 사용자 정의 빌드/테스트 워크플로 설정 가능.
주요 특징
모노레포를 쉽게 도입할 수 있게 해주는 도구들이 존재하지만, 별도의 설치가 필요 없고 러닝러브가 비교 적으며, 기본적으로 제공되는 yarn
의 내장 기능인 yarn workspaces
를 사용하여 Monorepo를 구축하기로 하였습니다.
package.json
{
"private": true,
"workspaces": [
"service",
"admin",
"component"
],
}
private:true
는 필수라는 점을 참고하세요. 작업 영역을 게시하기 위한 것이 아니므로 실수로 작업 영역이 노출되지 않도록 안전 조치를 추가했습니다.
최상단 루트에 workspaces
에 해당하는 폴더를 생성합니다.
생성한 해당 폴더 안에 package.json
파일 생성하여 코드를 작성해줍니다.
package.json
{
"name": "admin",
"version": "1.0.0",
"dependencies": {
"cross-env": "5.0.5",
"service": "1.0.0",
"@monorepo/component": "*",
"@monorepo/drrr-front": "*"
}
}
❗ 여기서 admin
폴더에서 service
폴더를 의존할려고 한다면 dependencies
에 추가하여 주면 됩니다.
yarn install
yarn install
명령어를 사용하면 각 폴더마다 node_modules
파일이 생성됩니다.
여기서 똑같은 package
를 추가할 경우 최상단 node_modules
에 폴더가 생성되며 호이스팅이 일어나게 됩니다.
"scripts": {
"@monorepo/component": "yarn workspace @monorepo/component",
"@monorepo/service": "yarn workspace @monorepo/service",
"@monorepo/admin": "yarn workspace @monorepo/admin"
},
root package.json
에 스크립트를 설정해주지 않으면 매번 프로젝트를 실행할 때 마다, cd ..
을 입력해야하는 번거로움이 생깁니다.
각 프로젝트의 package.json의 script를 root에서 경로 이동 없이 바로 사용할 수 있습니다. scripts의 key 값인 client와 server는 그 때 사용하는 용도입니다.
❗ 주의 사항
최상단 root package.json
은 monorepo
내 전체적인 Dependency
및 workspace
에 대한 정보를 가지고 있으며, 하위 디렉토리 내 각 프로젝트의 pakcage.json
에서 해당 프로젝트의 Dependency
를 관리하게 됩니다.
root package.json
에서 관리하는 Dendency
는 Monorepo
환경 자체에 적용되는 것이므로, 모든 프로젝트에 필요한 Dependecy
만 설정하는 것이 좋습니다.
yarn worskpaces
에서는 각 프로젝트의 중복되는 의존성 모듈을 최상단 node_modules
에서 공유하여 함께 사용할 수 있습니다.
yarn workspaces
이전에, 우선 하나의 프로젝트가 Hoisting
되는 방식에 대해 살펴봅시다.
위 package-1
의 Dependency
정보를 보면, A 모듈을 직접 의존하고 있고, A를 의존성으로 가지고 있는 C 모듈 역시 의존성 모듈로 이용하고 있습니다. 여러 곳에서 동일한 의존성을 가지는 모듈은 중복으로 설치될 필요가 없으므로, 함께 참조되어 사용할 수 있도록 node_modules
최상단 계층으로 Hoisting
됩니다.
Yarn Workspaces
에서는 위에서 설명한 방식처럼 중복되는 의존성 모듈이 최상단 node_modules
로 호이스팅되어 여러 프로젝트 간에 공유될 수 있습니다.
기본적으로 Monorepo
내 각 프로젝트는 자신만의 node_modules
폴더를 가지고 있습니다. 그러나 여러 프로젝트에서 중복되는 모듈은 최상단 node_modules
로 호이스팅되어 중복 설치를 방지하고, 하위 프로젝트 간에 모듈을 공유할 수 있습니다.
그러나 아쉽게도 현재 yarn workspace
에서 hoisting
을 이용한 Dependency
관리는 아직 많이 불안정한 상태라고 합니다.
yarn 공식문서에도 모든 타사 라이브러리들이 아직 monorepo
환경에 최적화가 되어 있는 상태가 아닌 만큼, 이러한 Can’t Found 문제는 쉽게 해결할 수 있는 문제가 아니라고 설명하고 있습니다. 따라서 yarn에서는 별도로 Hoisting
이 되지 않도록 noHoist
옵션을 제공합니다
"workspaces": {
"nohoist": ["react-native", "react-native/**"]
}
noHoist
옵션을 사용하면 해당 모듈들이 각 하위 프로젝트의 로컬 node_modules
에 설치되므로, Build
시 발생할 수 있는 not Found
오류를 방지할 수 있습니다.
Monorepo
를 처음 사용하면서 초기 설정에 많은 시간이 소요되었습니다. 블로그에서 제시한 대로 진행했지만, 여러 문제들이 발생해 어려움을 겪었습니다. 특히, workspace
간 의존성 문제나 hoisting
관련 이슈들이 빈번하게 발생했습니다.이 과정을 통해 monorepo
의 장점과 한계를 알게 되었고, 초기 설정에서 주의할 점을 배웠습니다. 여러 프로젝트를 효율적으로 관리할 수 있다는 장점이 있지만, 의존성 관리와 같은 부분에서 신중함이 필요하다는 점을 깨달았습니다. 이번 경험은 Monorepo
를 활용할 때 더 나은 설정과 관리 방법을 찾는 데 큰 도움이 되었고, 향후 비슷한 상황에서 더 잘 대응할 수 있을 것 같습니다.
📚 참고한 블로그