⚡ Yarn workspaces + Turborepo 를 이용한 프론트엔드 Monorepo 구축기 (Part 1)

Yoon Han
LBOX Team
Published in
15 min readJan 17, 2023

안녕하세요! 엘박스 프론트엔드 엔지니어 한 윤 입니다.
총 3편의 시리즈 아티클을 통해 Yarn workspaces 및 Turborepo 를 활용하여 Monorepo 프로젝트를 구축해 본 경험을 공유합니다!

Monorepo 구축 전체 과정과 구축 후 trouble shooting 경험까지 상세하게 작성하려 노력하였습니다. 최대한 쉽게 읽히도록 총 3편의 시리즈로 구성할 예정이니 재밌게 읽어주시면 감사하겠습니다!

본편(1편)에서는 모노레포와 관련한 제반 사항을 세팅하는 방법을 다루도록 하겠습니다.
모노레포 구축 과정을 단계별로 확인할 수 있는 GitHub repository 도 생성해 놓았으니 참조해주세요!

Table of Contents

  1. ❓ 모노레포(Monorepo)란
  2. 📁 디렉터리 생성하기
  3. ⚡ Yarn berry 세팅하기
  4. 📁 디렉터리 구조 살펴보기
  5. ⚡ 모노레포 제반 설정 완료하기
  6. 🏁 마치며

❓ 모노레포(Monorepo)란

🤔 모노레포가 무엇인가요

위키백과에 따르면 모노레포의 정의는 다음과 같습니다.

In version-control systems, a monorepo is a software-development strategy in which the code for a number of projects is stored in the same repository.

해석해보면 여러 프로젝트의 코드가 한 코드 저장소(repository)에 전부 들어가 있다는 뜻입니다.

일반적으로 한 프로젝트 당 하나의 저장소를 만들어 코드 베이스를 관리하는데,(Multirepo or Polyrepo) 그와 반대로 모노레포는 모든 프로젝트를 한 저장소에 담고 관리하겠다는 것이죠.

🤔 어떤 장점이 있나요

  • 새로운 프로젝트 생성이 편리합니다.
    멀티레포 환경에서는 새로운 프로젝트를 생성하고 세팅할 때 마다 다음과 같은 과정을 반복해야 합니다.
    저장소 생성 → boilerplate 코드 생성 및 디렉터리 구조 확립 → ESLintPrettier 같은 개발 관련 툴 설정 → CI/CD 설정 → npm registry 에 publish
    반면 모노레포에서는 이미 구축된 개발 환경 세팅을 프로젝트간에 공유할 수 있어서 새 프로젝트 구축에 소요되는 시간을 줄여줍니다. 또한 일부 프로젝트가 변경 되었을 때 업데이트 배포를 위하여 npm에 다시 publish 할 필요가 없습니다.
  • 손쉬운 코드 공유가 가능하여 자연스럽게 코드 중복이 억제됩니다.
    모노레포 환경에서는 모든 프로젝트가 하나의 저장소 내에 존재하기 때문에 손쉽게 특정 프로젝트로부터 코드를 import할 수 있어 프로젝트 간 코드를 공유하기가 매우 간단합니다.
  • 다른 프로젝트에서 발생한 변경 사항 파악이 용이합니다.
    멀티레포 환경이라면 각 프로젝트의 변경사항을 파악하기 위해 해당 프로젝트 저장소의 commit history 를 직접 살펴봐야 합니다. 주의를 기울이지 않을 경우 타 프로젝트에서 발생한 변경 사항을 인지하지 못하게 되는 구조이죠.
    반면 모노레포 환경에서는 모든 프로젝트에 관한 변경사항이 하나의 저장소에서 일어나기 때문에 본인이 현재 작업 중이지 않은 프로젝트에서 일어난 변경 사항도 자연스럽게 파악 할 수 있게 됩니다.

🤔 어떤 단점이 있나요

  • 새로운 코드 버전 관리 방식을 고민해야 합니다.
    모노레포 환경에서는 기존의 Git-flow 방식을 사용하여 버전 히스토리를 관리하기 어렵기 때문에 Trunk-based workflow 같은 새로운 버전 관리 방식에 대한 학습이 필요합니다.

지금까지 모노레포란 무엇인지와, 모노레포의 장·단점에 관해 가볍게 살펴보았습니다. 그럼 지금부터 실습을 통해 직접 모노레포를 구축해보도록 하겠습니다.

💡 들어가기 전에 💡

실습 과정의 환경은 다음과 같습니다.

- OS: Mac OS Ventura
- Browser: Google Chrome latest
- IDE: VS Code

📁 디렉터리 생성하기

선호하는 방식대로 원하는 위치에 디렉터리를 하나 생성해준 뒤 해당 디렉터리로 이동합니다. 저는 터미널을 이용했습니다.

mkdir monorepo-example && cd monorepo-example

이후 터미널에서 git init 명령을 통해 git 초기화를 진행합니다.

git init
git init 으로 git 초기화 진행

⚡ Yarn berry 세팅하기

git 이 세팅된 디렉터리를 만들었으니 이제 패키지 매니저를 설치할 차례입니다.
설치하기 전에 저희가 Yarn berry 를 선택한 이유를 간략히 말씀드리도록 하겠습니다.

❓ Yarn berry 를 선택한 이유

저희는 왜 Yarn berry(v2) 를 선택했을까요?

Yarn berry 가 npm 이 가진 여러 문제점들을 해결했기 때문인데요, 저희는 특히나 PnP(Plug’n’Play) feature 가 가장 매력적이라고 생각했습니다. 왜냐하면 이 PnP 시스템을 사용할 경우 배포 파이프라인에서 항상 병목이 되는 의존성 패키지 설치 과정(npm install or yarn add)이 필요없어지기 때문입니다. 이러한 특성 덕분에 배포에 소요되는 시간을 단축할 수 있게되죠.

❓ PnP with Zero-install 이란

압축된 zip 파일로 관리되고 있는 패키지들

Yarn berry 의 PnP 시스템은 각 의존성 패키지를 .zip 확장자를 가진 압축 파일을 통해 관리합니다.
이 압축 파일들은 용량이 상대적으로 작기에 git 으로 직접 관리하기에도 부담이 적습니다. 그래서 보통 PnP 시스템을 사용하게 되면 git remote repository 에 의존성 패키지들을 모두 올려(push)놓고 관리를 하게 되죠.

그럼 이 의존성 패키지들의 위치 정보는 어떤 파일에서 관리하고 있을까요?
바로 .pnp.cjs 파일입니다. .pnp.cjs 파일의 내용을 보면 각 의존성 패키지들의 위치를 packageLocation 속성에 기록 해놓고 있습니다.

🔄 Yarn berry 설치하기

이제 패키지 매니저인 Yarn 을 berry 버전으로 설치해보도록 하겠습니다.

혹시 아직 Yarn 을 설치 하지 않으셨다면, 먼저 npm install -g yarn 명령어를 통해 Yarn을 설치해주세요.

npm install -g yarn

이후에 yarn set version berry 명령어를 통해 berry 버전으로 설정해 주세요.

yarn set version berry
Yarn 버전 berry 로 교체

사용된 Yarn version 은 3.3.1 입니다.

📁 디렉터리 구조 살펴보기

저희가 지금부터 구축할 모노레포는 최종적으로 다음과 같은 디렉터리 구조를 따릅니다.

모노레포 디렉터리 구조

하나씩 살펴보겠습니다.

  • .yarn : Yarn 관련 파일들이 속해 있습니다. 위에서 yarn set version berry 명령어를 실행하셨다면 자동으로 생성됩니다.
  • .yarn/cache : 의존성 패키지들이 설치되어있는 디렉터리 입니다.
  • .yarn/releases : minify + uglify 된 Yarn 코드 파일이 속해 있습니다.
  • .yarn/sdks : Yarn 에서 자체적으로 Editor SDK 를 제공하는데, 해당 SDK 파일들이 속해 있습니다.
  • .yarn/unplugged : .yarn/cache 하에서 관리되던 의존성 패키지를 unplug 하게 되면 이 디렉터리로 들어오게 됩니다.
  • .yarn/install-state.gz : 현재 Yarn 프로젝트의 정확한 상태 정보를 저장하고 있는 바이너리 파일 입니다. git으로 관리하면 안되는 파일입니다.
  • apps : 서비스 workspace 들이 속하게 될 디렉터리 입니다. 이 디렉터리의 하위 디렉터리들은 각각 하나의 서비스 workspace 를 의미합니다.
  • apps/nextjs : Next.js 프레임워크를 사용한 workspace 디렉터리 입니다.
  • packages : 서비스 workspace 가 아닌 workspace 들이 속하게 될 디렉터리 입니다. 이 디렉터리의 하위 디렉터리들은 각각 하나의 workspace 를 의미합니다.
  • packages/storybook : Storybook 을 사용하는 workspace 디렉터리 입니다.
  • packages/shared : 모노레포 전반에서 공통적으로 사용될 코드들이 속하게 될 디렉터리 입니다.
  • .yarnrc.yml : Yarn configuration 파일입니다.
  • package.json : 모노레포의 메타 정보 및 의존성 패키지들에 관한 정보가 기록된 피일입니다.
  • .pnp.cjs : .yarn/cache 에 설치된 의존성 패키지들을 어떻게 참조해야 하는지에 관한 정보가 기록된 파일입니다.
  • .pnp.loader.mjs : PnP 시스템 하에서 의존성 패키지(모듈)들을 불러올 때 사용하는 코드가 포함된 파일입니다. 이 파일의 내용은 .pnp.cjs 파일의 내용에 포함됩니다.
  • yarn.lock : 의존성 패키지들 간의 의존 관계가 정확히 적혀있는 파일입니다. 현재 프로젝트에 속한 의존성 패키지들의 정확한 상태를 본 떠 놓은 파일이라고 생각하면 됩니다.
  • turbo.json : Turborepo configuration 파일입니다.
  • README.md : markdown 형식의 repository 소개 파일 입니다.
  • tsconfig.base.json : 모노레포 하위의 각 workspace 안에 있는 tsconfig 파일들이 참조할 파일입니다.
  • monorepo-example.code-workspace : VS Code 에서 사용할 VS Code workspaces configuration 파일입니다.
  • .eslintrc.js : 모노레포 root 의ESLint configuration 파일 입니다.
  • .prettierrc.js : 모노레포 root 의Prettier configuration 파일 입니다.

⚡ 모노레포 제반 설정 완료하기

저희가 구축하게 될 모노레포의 최종 디렉터리 구조를 살펴보았습니다.
그럼 이제 Yarn berry 관련 설정 및 모노레포 제반 설정을 진행해 보도록 하겠습니다.

📄 .yarnrc.yml 파일 작성하기

monorepo-example/.yarnrc.yml 파일에 다음 내용을 작성해 주세요.

nodeLinker: pnp

yarnPath: .yarn/releases/yarn-3.3.1.cjs

nodeLinker 라는 속성을 통해 우리가 PnP 시스템을 사용할 것이라고 Yarn 에 명시적으로 알려줍니다. yarnPathYarn 의 위치를 담고 있습니다.

📄 package.json 파일 작성하기

{
"name": "monorepo-example",
"private": true,
"packageManager": "yarn@3.3.1",
"workspaces": [
"apps/*",
"packages/*"
]
}

monorepo-example/package.json 파일에 위와 같이 내용을 적어줍니다.

여기에서 중요한 부분은 workspaces 부분인데요 이곳에 저희가 구성할 디렉터리 구조에 맞추어 내용을 작성해 주어야 이후에 진행할 단계에서 오류가 발생하지 않습니다.
만약 workspaces 부분을 작성해 주지 않는다면, monorepo-example/apps/ 하위에 create-next-app 를 이용하여 프로젝트를 생성할 때 모노레포 구조임을 인지하지 못하여 에러가 발생하게 됩니다.

이렇게 작성해주신 뒤에 모노레포 root 위치에서 yarn install 명령어를 한 번 실행시켜서 .pnp.cjs 파일과 yarn.lock파일을 생성해 주세요.

yarn install

📄 monorepo-example.code-workspace 파일 작성하기

{
"folders": [
{
"path": "apps/lbox-client-nextjs"
},
{
"path": "packages/lbox-design-system"
},
{
"path": "packages/shared"
}
],
"extensions": {
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
]
},
}

이 파일은 VS Code 에서 workspaces 개념을 사용하기 위해 필요한 파일입니다(Yarn workspaces 와는 다릅니다!).

monorepo-example/monorepo-example.codeworkspace 파일에 위의 내용을 적어줍니다.

각 필드에 관한 설명은 다음과 같습니다.

  • folders : 배열의 각 path 속성에 명시된 디렉터리는 VS Code 상에서 각각의 독립된 workspace 로 인식됩니다.
  • extensions : recommendations 필드에 해당 모노레포와 함께 사용하면 유용한(또는 같이 사용해야만 하는) VS Code Extension 들의 목록을 정의합니다.

또한 이 파일을 VS Code 에서 열면, path 에 명시된 workspace 들을 독립적으로 띄울 수 있는 버튼을 제공합니다.

workspace 별로 project explorer 를 독립적으로 띄울 수 있게 해주는 버튼 (하단)

시리즈 Part 3 까지의 구축 과정을 모두 마치신 뒤에 해당 버튼을 누르게 되면 아래와 같이 VS Code explorer 영역의 구성이 변하는 것을 확인하실 수 있습니다.

현재는 각 workspace 들을 생성하기 이전이므로 버튼을 눌러도 아래와 같은 내용은 동일하게 보이지 않습니다!

독립적으로 열린 각각의 workspace

📄 .gitignore 파일 작성하기

monorepo-example/.gitignore 파일 내용을 아래와 같이 작성해 주세요.

# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# local editor settings
/.vscode

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# vercel
.vercel

# turbo
.turbo
build/**
dist/**
.next/**

# typescript
*.tsbuildinfo
next-env.d.ts

# node_modules
node_modules
**/node_modules

# yarn berry w/ zero-install
.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

특히 중요한 부분은 # yarn berry w/ zero-install 부분입니다.
이 부분은 Yarn berry PnP 시스템(w/ zero-install)을 사용하는 환경에서 .yarn/디렉터리 안의 어떤 디렉터리들을 git 으로 관리해야 하는지 명시합니다.

💿 ESLint VS Code Extension 설치하기

VS Code 에디터 환경에서 ESLint 를 구동하기 위한 Extension 을 하나 설치하도록 하겠습니다.

VS Code Extension Marketplace 에서 ESLint 를 검색하여 설치해 주세요.

VS Code Marketplace (ESLInt)

💿 Prettier VS Code Extension 설치하기

이번에는 Prettier 를 구동하기 위한 Extension 도 하나 설치하도록 하겠습니다.

VS Code Extension Marketplace 에서 Prettier 를 검색하여 설치해 주세요.

VS Code Marketplace (Prettier)

🏁 마치며

여기까지, 모노레포를 구축하기 위해 필요한 여러 가지 사전 설정을 완료해 보았습니다!
다음 편(Part 2)에서는 본격적으로 하위 workspace 들을 구성해 보도록 하겠습니다.
긴 글 읽어주셔서 감사합니다 😄

> 다음 글 보기

✏️ References

--

--