모노레포란?
두 개 이상의 프로젝트 코드를 하나의 버전 관리 저장소(레포지토리)에서 관리하는 방법
등장 이유 : 큰 규모의 소프트웨어 개발 프로젝트에서 발생하는 문제를 해결하기 위해서
큰 규모의 프로젝트에는 여러 개의 레포가 생성됨. 표면적으로 봤을 때는 큰 문제는 없으나 다음과 같은 문제들 때문에 프로젝트 관리에 어려움이 발생할 수 있음
- 중복 코드
- 서로 다른 패키지 의존성
- 서로 의존하는 프로젝트들끼리의 리펙토링 비용
- 코드가 저장소마다 상황이 다르기에 협업 문제
특징
- 단일 코드 저장소 : 모든 소스 코드를 단일 코드 저장소에 저장
- 모듈화 : 코드를 모듈화해 필요한 모듈만 가져와서 사용
- 의존성 관리 : 모든 코드가 단일 코드 저장소에 있기에 의존성 문제 빠르게 해결
- 협업 : 모든 코드가 단일 콛 저장소에 있기에 협업 편리성 증가
- 코드 품질 : 리팩터링시 모든 프로젝트 상황 고려 → 코드 품질 일관성 유지
단점
- 복잡성 증가 : 여러 프로젝트를 하나의 저장소에서 관리하므로 코드 충돌 등의 문제 발생 가능성 증가
- 빌드시간 증가 : 모든 프로젝트를 함께 빌드해야 하므로 빌드시간이 증가
- 변경 사항이 모든 프로젝트에 영향 : 하나의 프로젝트에서 발생한 변경 사항이 전세 시스템에 영향
모노레포 방식은 프로젝트 규모, 복잡성, 의존성 등을 고려해 적용해야 함
- 작은 프로젝트 : 모노레포 방식이 오버 엔지니어링처럼 느껴질 수 있음
- 다른 언어를 사용하는 프로젝트 : 서로 다른 언어로 개발된 프로젝트는 모노레포 방식으로 통합하기 어려움
- 서로 다른 배포 주기 가진 경우 : 모노레포보다는 멀티 레포 방식이 더 적합
MonoRepo with yarn Berry 구축 실습
yarn 설치
$ npm install -g yarn
yarn classic(yarn v1)과 yarn berry(yarn v2)는 버전 차이가 많이 나므로 yarn berry로 추가적인 설정 작업
$ yarn set version berry
cf) yarn set version berry 명령어 부분에서 error: unable to get local issuer certificate 이 오류가 발생한다면,
$ yarn config set strict-ssl false
위 사진처럼 명령어 입력 후 재시도
이후
$ yarn -v
명령어를 통해 yarn 2.0 이상의 버전이 설치되었는지 확인 (본인의 경우 yarn@4.7이 설치됨)
$ yarn init
알맞게 버전 설치가 완료되었다면 위 명령어로 프로젝트 설정을 마무리
cf) 위 명령어 실행 시 package.json을 비롯한 yarn 설정 파일들이 생성됨
package.json 파일에 아래와 같이 작업할 모노레포의 name과 workspace를 추가 (name은 자유롭게 설정 가능)
{
"name": "monorepo-example",
// 이 부분의 명령어를 통해 package 내부의 모든 폴더는 workspace에 속하도록 설정됨
"workspaces": {
"packages": [
"package/*"
]
},
"packageManager": "yarn@4.7.0",
}
위와 같이 작성을 마친 뒤, package폴더 생성
지금부터 생성할 project 코드들은 모두 package 폴더 내부에서 관리됨
$ cd package
위 명령어를 통해 package 폴더로 이동,
CRA 또는 vite로 프로젝트를 생성
create-react-app
> $ npx create-react-app front-mono
vite
> $ yarn create vite
cf) 빌드 속도가 빠르고 번들러 사이즈가 작은 vite를 선택함
(vite 설정 : front-mono(name) → react → Typescript 설정)
다음으로 package 폴더 내부에 common 폴더를 생성 생성이 완료되었다면 현재 폴더 구조가 다음과 같은지 확인한 뒤 5️⃣를 진행
📦MONOREPO-EXAMPLE
┣ 📂.git
┣ 📂.yarn
┣ 📂package
┃ ┣ 📂common
┃ ┗ 📂front-mono
┣ 📜.editorconfig
┣ 📜.gitignore
┣ 📜.yarnrc.yml
┣ 📜README.md
┣ 📜package.json
┗ 📜yarn.lock
4️⃣에서 생성한 프로젝트 파일이 정상적으로 실행되는지 확인
생성한 폴더로 이동해 yarn(yarn install)과 yarn dev(vite 개발 서버 실행) 명령어 입력
$ cd front-mono
$ yarn && yarn dev
정상적으로 프로젝트가 실행된다면 모노레포 구축을 위한 프로젝트 서버 셋팅이 완료된 것
package/front-mono/src/App.tsx에 접근해보면 typescript가 적용되지 않아 에러가 발생함
본 문제는 패키지매니저 yarn berry에서와 npm에서 모듈을 불러오는 방식의 차이가 있기에 발생하는 오류
따라서 typescript와 eslint, prettier를 설치해준 뒤, vscode를 사용 중인 경우에 한해 sdk를 설치해줌
루트 디렉토리로 이동, 아래 명령어 입력
$ yarn add -D typescript prettier eslint
$ yarn dlx @yarnpkg/sdks vscode
※ sdk부터 설치할 경우 문제가 발생할 가능성이 높음
※ 반드시 typescript와 같이 1️⃣ 문법에 영향을 주는 package를 먼저 설치한 뒤 2️⃣ sdk를 설치해야 함!
command + shift + p를 통해 typescript 버전을 선택하는 창을 열고 Use Workspace Version(sdk)을 선택
monorepo를 통해 프로젝트 간 코드를 공유할 수 있는 동시에 5️⃣에서 진행하였던 개발 환경 설정 내용 역시 공유할 수 있음
최상위 폴더(루트)에 tsconfig.base.json파일을 생성
tsconfig.base.json 파일의 내용을 아래와 같이 작성
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
}
다음으로 package/front-mono 폴더 내에 위치하는 tsconfig.json의 내용을 아래처럼 수정
{
"compilerOptions": {
},
"include": ["./src"],
"extends": "../../tsconfig.base.json",
}
위의 수정사항을 통해 tsconfig.base.json에서 설정한 내용을 extends해 사용할 수 있음
현재 package/common폴더는 비어있는 상태
common 폴더로 이동한 뒤, 아래 명령어를 통해 package.json 파일을 생성함
$ yarn init
생성된 package.json 파일 아래처럼 수정
{
"name": "@monorepo/common",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"author": "",
"license": "ISC"
}
수정 이후, 프로젝트간 공유하게 될 코드인 index.ts 생성
index.ts 파일에는 front-mono와 common 프로젝트에서 코드를 공유할 수 있음을 간단하게 확인하기 위해 임의의 변수를 선언
const str: string = "monorepo를 통해 공유할 변수";
export default str;
common 폴더 구조
📦common
┣ 📜README.md
┣ 📜index.ts
┗ 📜package.json
package/common 폴더에 공유할 코드를 만들어 주었으니 package/front-mono 폴더에서 이를 가져와 사용할 수 있도록 package.json파일을 수정해야 함
- package/front-mono/package.json
...
"dependencies": {
"@monorepo/common": "*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
...
dependencies 부분에 common의 package명을 입력해주고 버전의 경우 *로 설정
이후 루트 또는 front-mono로 이동, 아래 명령어로 project간 연결 작업 시작
$ yarn
- package/front-mono/src/App.tsx
import React, { useState } from 'react'
import logo from './logo.svg'
import './App.css'
import str from '@monorepo/common'
function App() {
const [count, setCount] = useState(0)
console.log(str)
...
return (
<div className="App">
...
<h1>{str}</h1>
...
</div>
)
}
위와 같이 @monorepo/common으로부터 선언해준 변수 str을 import해 사용할 경우,
위 사진처럼 프로젝트 간 코드 공유가 자유롭게 잘 작동함을 확인할 수 있음