1. Monorepo
Monorepo
는 Monolithic Repositories
의 약자로, 직역하면 단단히 하나로 짜여진 저장소
를 의미합니다. 단단히 하나로 짷여진 저장소라는게 무슨 의미 일까요? 일반적으로 보통 하나의 프로젝트는 하나의 레포지토리와 매칭이 되고 이런 형태의 프로젝트 구성을 Multi-repo
, PolyRepo
라고 합니다.
A 프로젝트 -> A 레포지토리
B 프로젝트 -> B 레포지토리
Monorepo
는 이와 반대로 여러 프로젝트가 하나의 레포지토리에 저장되는 형태를 생각 할 수 있습니다.
A 프로젝트
B 프로젝트 -> Mono 레포지토리
C 프로젝트
그림으로 표현해보면 아래와 같은 구조입니다.
2. Monorepo를 사용하는 이유
Monorepo를 사용하는 이유는 굉장히 다양할 수 있는데요, 장점이 있는 만큼 단점도 있을 수 있어서,
프로젝트 환경을 고려해 결정하면 좋을것 같습니다.
2.1. 장점
2.1.1. 하나의 레포지토리로 여러개의 프로젝트를 관리할 수 있다.
하나의 레포지토리가 여래개의 프로젝트(여기서는 패키지라고 하겠습니다.)를 포함하고 있는것은 굉장히 큰 편의성을 가집니다. 코드를 짜는 입장에서도 여러개의 IDE를 열거나, IDE에서 프로젝트를 스위치해가며 개발할 필요없이 하나의 IDE에서 하위폴더로 구분된 여러 패키지들의 코드를 작성할 수 있습니다.
2.1.2. 중첩되는 코드를 공통화할 수 있다.
A, B, C, D
패키지로 구성된 Monorepo가 있고, 여러 프로젝트들이 공통으로 사용해야하는 로직이 있을 때, 이를 쉽게 추가적인 E
패키지로 분리하고 A~D
패키지에서 import
하여 사용할 수 있습니다.
2.1.3. 중첩되는 모듈은 하나만 설치하여 사용합니다.
A, B, C, D
패키지 모두 react 16.11.0
버전을 사용한다고 가정하면, 각각의 패키지에서 react 16.11.0 버전을 설치하는것이 아닌 root에 react 16.11.0
버전 하나를 설치하고 A~D
에서 끌어다 사용합니다.
2.2. 단점
2.2.1. 의존성의 충돌이 생길경우, 정상동작 하던 패키지들도 동작하지 않을 수 있다.
특정 패키지가 특정 버전의 모듈을 필요로 하는 경우, 다른 버전의 모듈을 사용하는 패키지와 의존성 충돌이 발생할 수 있습니다.
2.2.2. 여러 프로젝트를 하나의 레포지토리로 관리하기 때문에 오히려 관리가 어려울 수 있다.
Monorepo로 관리하는 패키지가 많지 않을 경우에는 해당되지 않지만, 관리하는 패키지가 증가함에 따라 오히려 가독성이나 여러가지 측면에서 비효율적이게 될 수 있습니다.
2.2.3. 초기 프로젝트 설정이 오래걸린다.
Monorepo로 포함되는 모든 프로젝트를 사용한다면 상관없지만, 그 중 일부만 필요한 경우에도, 전체적인 node_module 설치가 이루어 져야한다.
3. Monorepo를 구성하는 여러가지 방법
Monorepo를 구성하는 방법은 아래와 같이 여러가지 방법들이 있으며, 저는 현재 yarn workspace 기능만을 사용하고 있습니다.
3.1. yarn workspace
node package manager
중에 하나인 yarn
에서는 (npm
엔 없음!!) workspace 기능을 통해서 monorepo를 가능하게 해줍니다.
3.2. Lerna
Lerna는 node moudule로, yarn의 workspace와 마찬가지로 monorepo를 가능하게 해주는 기능을 제공함과 동시에, 설치된 의존성을 제거해주는 clean
기능이나 monorepo로 구성한 package를 npm 배포할 수 있는 기능들을 제공합니다.
3.3. git submodule
기존 multi-repo로 관리되는 것들을 하나로 합쳐서, monorepo를 구성하려는 경우 git의 submodule을 사용하여 구성 할 수 있습니다. 단 git submodule은 위의 방법들과는 다르게 다수의 레포지토리를 관리하여야 하고 submodule이라는 것의 개념과 사용방법을 익혀야하기 때문에 어느정도 러닝 커브가 있습니다.
4. yarn workspace로 Monorepo 구성하기
yarn의 workspace 기능을 통해서 monorepo를 구성하는 방식은 아주 간단합니다.
yarn init
을 통해서 프로젝트 초기화를 해주고, package.json에 아래와 같이 private
과 workspaces
프로퍼티를 설정해주면 됩니다.
// package.json
{
...
"private": true,
"workspaces": [
"packages/*"
],
...
}
4.1. 의존성 관리 방식
위의 방식대로 설정을 마치고, 패키지를 구성한뒤 의존성을 설치하게 되면 패키지 각각의 패키지에서 요구하는 모듈의 버전을 확인하여 공통되는 부분은 root의 node_modules폴더 안에 설치를 하고, symlink를 통해 각각의 패키지로 연결되어 사용됩니다.
5. yarn workspace를 사용한 이유
위에 설명했엇던 방식들(yarn workspace, lerna, git submodule)로 비교를 해보자면, lerna는 npm 배포와 관련된 유용한 유틸 기능들을 제공해주지만 저희는 npm 배포를 하지 않았기 때문에 사용할 필요가 없다고 판단했고 git submodule 또한 기존의 multi-repo로 관리되던 프로젝트를 monorepo로 구성하는 것이 아닌 시작부터 monorepo로 시작하였기 때문에, monorepo를 구성하는데만 필요한 기능인 yarn의 workspace 기능을 사용하였습니다.
6. Trouble Shooting 사례
yarn의 workspace는 패키지들 사이에서 같은 버전을 사용하는 버전을 찾아 root의 node_modules안에 설치하는데, 이렇게 한번 완성된 형태의 의존성 구조를 가진채로, 기존 의존성을 해치는 새로운 패키지가 추가되었을경우 기존 잘 동작하던 패키지가 동작하지 않거나 하는 사이드 이펙트가 발생 할 수 있습니다.
하나의 예시로, monorepo로 관리하는 공통 components
를 담당하는 패키지에서, storybook의 커스텀 웹팩 세팅을 위해서 babel-loader 8.22.0
버전을 설치해 사용하고 있었고, 이후 추가로 create-react-app
을 통해서 새로운 패키지를 추가하였는데, cra를 통해 생성된 패키지는 babel-loader 8.1.0
을 사용해야하는 제약사항이 있었던 문제로 인해 실행이 되지 않은 문제가 있었습니다.
위 문제를 해결하기 위해서 components
패키지의 babel-loader
버전을 8.1.0
으로 다운그레이드
하여 해결 할 수 있었습니다.
Top comments (0)