Monorepo란?
Monorepo는 두 개 이상의 프로젝트 코드를 동일한 저장소에 저장하는 소프트웨어 개발 방법입니다. 각 프로젝트별로 패키지, 라이브러리, UI 컴포넌트를 저장하는 기존 방식과 달리 Monorepo에서는 패키지, 라이브러리, UI 컴포넌트 등을 공통을 관리할 수 있습니다. 또한, 프로젝트를 개발하는 개발자들이 하나의 저장소만 바라보기 때문에, 협업에 유리합니다.
Monorepo를 구축하기 위한 도구
Monorepo를 구축하기 위한 대표적인 도구로는 아래 4가지가 있습니다.
Yarn berry와 Yarn workspace를 이용하는 방법은 간단하게 Monorepo를 구성할 수는 있지만, 서비스 규모가 커지면 설정을 추가하는 작업이 있습니다.
Lerna는 가장 많이 사용되고 있는 방식이었지만, 2022년 5월에 Nx를 관리하고있는 Nrwl에 소유권을 넘기면서 더 이상 유지보수하지 않는다고 밝혔습니다.
Nx는 Nrwl에서 개발한 프로젝트로 여러가지 FE 라이브러리를 지원하고 있는 대규모의 프로젝트입니다. 지원하는 기능이 많고, 프로젝트도 오랫동안 유지되고 있어서 안정적으로 진행할 수 있는 방법 중 하나입니다. 하지만, build, 라이브러리를 설치하는 명령어가 Nx에 특화된 명령어로 다소 헷갈릴 수 있는 단점이 있습니다.
Turborepo는 가장 최근에 출시되었고, Vercel에서 개발하고 운영하고있는 프로젝트입니다. zero config를 지향하고 있고, 병렬 처리기법, remote caching, pipeline 등 여러 기능을 갖추고 있습니다. 하지만, 최근에 출시된 만큼 레퍼런스가 적고, Nx보다는 지원하는 기능이 적은 단점이 있습니다.
저희는 Lerna를 제외한 3가지 선택지 중에서 Turborepo를 선정했습니다. Turborepo를 선정한 이유로는 초기 설정이 쉽고, 병렬처리 기법과 캐싱 뿐만 아니라 원격 캐싱을 이용해서 빌드 속도가 빠르다는 점이 가장 끌렸습니다. 그리고 Next.js를 개발하는 Vercel에서 관리하고 있고, Vercel에서 설정이 편리하다는 점도 끌렸습니다.
Turborepo를 이용한 Monorepo 구축 방법
초기 설정
Turborepo를 이용한 Monorepo를 구성하기 위해서는 터미널에서 아래 명령어를 실행하면 됩니다.
npx create-turbo@latest
위 명령어를 실행하면 프로젝트의 이름과 어떤 패키지 관리자를 설정할 지 물어보고, 다음과 같은 프로젝트가 구성됩니다.
- apps: Monorepo 내에서 실행되는 앱입니다.
- packages: 프로젝트에서 공통으로 사용할 패키지입니다. eslint, tsconfig, ui가 기본 설정되어 있습니다.
- turbo.json: build나 test, lint 등을 실행할 때 실행할 pipeline을 설정할 수 있는 파일입니다.
개발 모드로 로컬에서 서비스를 실행하기 위해서는 아래 명령어를 사용합니다.
npx turbo run dev
명령어를 실행하면 apps에 있는 서비스를 병렬로 실행되고, 아래와 같이 2개의 서비스가 3000, 3001 port에 실행됩니다.
캐싱(Caching)
Turborepo를 사용하는 가장 큰 이유이자 장점은 캐싱입니다.
최초에 서비스 빌드 명령어를 실행하면 아래와 같이 확인할 수 있습니다.
npx turbo run build
이렇게 빌드를 실행하면, node_modules/.cache/turbo 디렉토리가 생성되고 디렉토리 내에 빌드 캐시가 저장됩니다.
이 상태에서 빌드를 다시 실행하면 수정된 내용이 없기 때문에, 저장된 캐시를 이용하여 빌드를 실행합니다.
아무것도 개발하지 않은 초기 프로젝트이지만, 최초 빌드에서는 8초가 걸렸고, 두번째 빌드에서는 캐시를 활용하여 146ms만에 빌드가 완료되었습니다.
여기에서 하나의 서비스만 수정을 하고, 다시 빌드한다면 아래와 같이 수정되지 않은 서비스는 캐시를 이용해서 빌드하고, 수정된 서비스를 다시 빌드를 진행합니다.
Monorepo 환경에서 개발할 때, 특정 서비스만 개발하고 배포하는 경우가 많기 때문에 Turborepo의 캐싱은 엄청난 장점을 갖고 있습니다.
원격 캐싱(Remote Caching)
Turborepo의 원격 캐싱은 Vercel의 캐싱 서버나 소유하고 있는 캐싱 서버를 연동하여 빌드 캐시를 저장하여 CI에 활용할 수 있는 방법입니다.
여기에서는 Vercel에서 빌드하는 경우, 어떻게 동작하는지 소개하겠습니다.
remote caching을 위해서 사전에 설정해야하는 부분이 몇 가지 있습니다.
[turbo pipeline에 cache outputs 설정]
각 서비스별 build의 output을 turbo.json에 설정합니다.
{ "$schema": "https://turborepo.org/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**"] }, "web#build": { "dependsOn": ["^build"], "outputs": [".next/**"] }, "docs#build": { "dependsOn": ["^build"], "outputs": [".next/**"] }, "lint": { "outputs": [] }, "dev": { "cache": false } } }
[Vercel 프로젝트 설정]
Vercel에 각 app의 프로젝트를 생성하고, 아래와 같이 Root Directory와 Build Command를 설정합니다.
[Vercel 계정 및 프로젝트 연동]
프로젝트의 터미널에서 vercel 계정과 연동합니다.
npx turbo login
그 다음 프로젝트를 연결하고 원격 캐싱에 연결합니다.
npx turbo link
[동작 확인]
이제 Github에 수정한 내용을 push 하면 Vercel에서 build가 자동으로 동작하고, 원격 캐싱 기능이 자동으로 활성화됩니다.
최초에는 저장된 캐시가 없기 때문에 33초가 소요됐습니다.
해당 App이 수정되지 않은 상태에서 Github에 다시 push된다면, Vercel에 저장된 원격 캐시를 이용해서 빌드가 동작합니다.
캐시를 이용해서 단 10초만에 빌드/배포가 완료되었습니다.
지금까지 Turborepo를 이용하여 Monorepo를 구축하는 방법에 대해서 설명하였습니다.
Turborepo를 이용하면 아주 간단한 명령어를 통해서 monorepo를 구축할 수 있고, 캐시의 장점을 이용해 빠르게 빌드 및 배포할 수 있습니다.
다음으로 Monorepo에서 사용 중이었던 yarn classic을 yarn berry로 변경한 경험을 공유하겠습니다.
yarn berry란?
yarn berry는 2020년 1월에 출시된 패키지 관리도구로, yarn classic에서 업그레이드된 버전입니다. yarn berry에서는 Plug’n’Play(pnp)라는 기능을 통해서 기존에 사용되던 node_modules을 사용하지 않고, .pnp.cjs라는 파일을 사용해 패키지의 의존성을 관리합니다.
yarn berry에서는 zero-install이라는 기능을 지원하는데, 이 기능은 프로젝트에서 사용되는 패키지를 zip으로 묶어서 캐시로 저장하고, 프로젝트를 빌드하고 배포할 때, 캐시로 저장되어 있는 패키지를 사용하기 때문에 빌드 속도를 크게 개선할 수 있습니다. 그리고 zip 파일로 저장되기 때문에 node_modules 보다 디스크의 공간을 적게 차지합니다.
yarn berry 적용 방법
먼저, yarn berry의 기능을 온전히 사용하려면 pnp 기능을 사용해야 하는데, 현재 Turborepo에서는 yarn berry의 pnp 기능을 완전히 지원하지 않습니다. 그래서 yarn berry에서 제공하는 nodeLinker를 이용해 node_modules를 이용합니다. Turborepo는 최신의 기술이고 개발도 빠르게 진행되기 때문에, 조만간 지원하리라 믿습니다!🙏🏻
yarn berry를 적용하기 위해서는 프로젝트 터미널에서 아래 명령어를 입력하면 간단하게 적용할 수 있습니다.
yarn set version berry
명령어를 실행하면 프로젝트 루트에 있는 package.json의 packageManager의 값이 변경되고, .yarn 폴더와 .yarnrc.yml이 생성됩니다.
yarn berry의 설정은 .yarnrc.yml에서 설정하면 됩니다. Turborepo는 pnp기능을 아직 지원하지 않기 때문에 nodeLinker를 node_modules로 설정합니다.
yarnPath: .yarn/releases/yarn-3.2.3.cjs
nodeLinker: node-modules
그 다음, 터미널에서 패키지를 설치하면, .yarn/cache에 설치된 패키지의 캐시 파일이 저장됩니다.
yarn install
yarn berry에서 제공하는 기능인 zero-install을 이용하기 위해서는 저장소에 캐시를 포함해야 하기 때문에 .gitignore에 아래 값을 추가합니다.
# yarn berry - zero install
.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
이렇게 monorepo에 yarn berry를 간단하게 설정할 수 있습니다.
Github에 코드를 push하고, Vercel의 빌드를 확인해보면, 캐시된 패키지를 사용하는 것을 확인할 수 있습니다.
이렇게 Turborepo와 yarn berry를 사용한 monorepo 구축 경험을 공유했습니다.
Turborepo에서는 아직 yarn berry의 pnp를 지원하지 않기 때문에, yarn classic을 사용해도 빌드/배포에 문제는 없습니다. 하지만 yarn classic의 유지보수가 종료된 상황에서 언제 사라질 지 모르기 때문에 yarn berry를 적용하는 것도 나쁘지 않다고 생각합니다.
또한, 프로젝트의 규모가 점점 커지면 Turborepo와 yarn berry의 장점을 더욱 잘 느낄 수 있을거라 생각합니다.
'IT > react' 카테고리의 다른 글
[번역][Next.js] Layout RFC (0) | 2022.07.29 |
---|---|
[react-query] useEffect에서 react-query의 useMutation을 사용할 때 dependencies 으로 인한 무한 요청 해결방법 (0) | 2022.07.13 |
CRA로 React 설치할 때 create-react-app 버전 이슈 (0) | 2022.02.21 |
Next.js에서 styled-component를 사용하기 위한 방법 (0) | 2021.12.06 |
react-router-dom v5, v6 비교 (1) | 2021.12.04 |
댓글