sharingStorage

모노레포 도입기 feat: pnpm, turborepo 본문

Front-End

모노레포 도입기 feat: pnpm, turborepo

Anstrengung 2025. 7. 1. 03:04

모노레포란? (모노레포, 멀티레포)

출처 : https://engineering.linecorp.com/ko/blog/monorepo-with-turborepo

모놀리식 어플리케이션

모노레포를 설명하기 전에 모놀리식 어플리케이션과 멀티레포에 대한 개념을 알아야합니다.

모노레포가 등장한 이유는 모놀리식 애플리케이션의 한계에 대한 비판에서 출발합니다.

우리가 일반적으로 사용하는 모놀리식 방식은 소스코드를 모듈화하지 않고 하나의 레포지터리에 모두 넣은 구조이며 모든 코드가 단일 버전으로 직접 의존하기 때문에 코드 재사용이 용이하고 빌드 및 배포 과정도 단순하지만, 관심 분리가 어렵고 기능 추가나 삭제가 전체에 영향을 주어 모든 작업이 거대한 단위로 처리된다는 단점이 있습니다.

멀티레포

이러한 문제를 해결하기위해 모듈화가 제안되었고 재사용하기 위한 프로그램을 모듈화하여 독자적인 저장소에 위치시키고 여러 레포지터리를 관리하는 것을 멀티레포 구조라고 합니다.

멀티레포는 말그대로 독립적인 여러개의 저장소가 있는 프로젝트고 각 프로젝트마다 각자의 개발, 린트, 배포과정, 테스트가 존재합니다.

이러한 멀티레포는 아래와 같은 문제점을 보입니다.

  • 각 팀이 유기적으로 협업해야할 때 각 프로젝트의 설정이 다르고 의존성 버전이 다르다면 유지보수가 어렵다
  • 중복 코드가 늘어난다.
  • 배포과정과 린트, 테스트 등 각 프로젝트 수만큼 따로 파이프라인을 관리해야한다.
  • 코드컨벤션, 각기 다른 명령어 사용으로 개발자 경험이 저하된다.
  • 관련 패키지의 변화가 다른 여러 저장소의 변화로 이어지는데 어려움이 있다.

이러한 문제들로 모노레포가 등장하였고 각 프로젝트들은 독자적인 프로젝트로 존재하지만 모노레포라는 하나의 저장소에 위치합니다.

모노레포

단일 레포지터리에 여러개의 서브 프로젝트가 존재하는 방식입니다.

모노레포는 이와 같이 모놀리식의 장점과 멀티레포의 단점을 보완하는 구조라고 볼 수 있습니다.

모노레포는

  • 더 쉬운 프로젝트 생성 (아래와 같은 과정을 모든 프로젝트에서 거치지 않아도 됩니다.)
    • 저장소 생성 > 커미터 추가 > 개발환경 구축 > CI/CD 구축 > 빌드 > 패키지 저장소에 publish
  • 더 쉬운 의존성 관리
    • 의존성 패키지가 같은 저장소에 있으므로 버전이 지정된 패키지를 npm registry와 같은 곳에 publish할 필요가 없습니다.
  • 프로젝트들에 걸친 원자적 커밋
    • 커밋할 때마다 모든 것이 함께 작동합니다. 변경 사항의 영향을 받는 조직에서 쉽게 변화를 확인할 수 있다.
  • 서로 의존하는 저장소들의 리팩터링 비용 감소
    • 모노레포는 대규모 변경을 훨씬 더 간단하게 만듭니다. 100개의 라이브러리로 만든 10개의 앱을 리팩터링하고 변경을 커밋하기 전에 모두 작동하는지 확인할 수 있습니다.
  • 테스트 및 빌드 범위 최소화
  • 소스 변경 시 모든 프로젝트를 다시 빌드하거나 다시 테스트하지 않는다. 대신 변경 사항의 영향을 받는 프로젝트만 다시 테스트하고 빌드한다.

Why 모노레포?

저희 Lococo 서비스는 일본 현지 K-뷰티 리뷰 플랫폼입니다.

해당 서비스에서는 추후 admin 개발이 확정된 상황에서 빠르고 일관적인 개발을 위해 디자인 시스템 도입을 결정하였습니다.

이러한 디자인 시스템을 npm registry로 별도 배포하는 대신 모노레포로 관리함으로써 admin, web, design-system(ui) 개발 시 일관적인 경험을 제공하고 코드 공유를 용이하게 하기 위해 모노레포를 선택하였습니다.

또한 각 패키지의 배포 파이프라인을 통합하여 관리 리소스를 효율적으로 줄일 수 있는 최적의 선택지로 판단되었습니다.

 

HOW 모노레포 적용방법

저는 모노레포 세팅을 돕는 다양한 선택지 중에 Turborepo와 pnpm을 함께 사용하는 것을 선택했습니다.

Turborepo

Vercel이 인수한 JavaScript와 TypeScript 코드 베이스의 모노레포를 위한 고성능 빌드 시스템입니다.

해당 시스템은 모노레포 환경에서 개발자가 더 쉽고 빠르게 도구를 사용할 수 있게 제공하는 것이기에 개발자는 복잡한 설정과 스크립트를 신경쓰는 대신 개발에 더 집중할 수 있습니다.

제 경험상 배포 플랫폼이 인수하거나 운영중인 도구는 호환성 측면에서 항상 이점을 가지고 있었고 역시나 아래와 같이 vercel에서 turborepo에 대한 예시코드, 가이드라인 문서도 잘 제공되어 있는 것을 확인했습니다.

 

turborepo/examples/with-tailwind at main · vercel/turborepo

Build system optimized for JavaScript and TypeScript, written in Rust - vercel/turborepo

github.com

Turborepo의 캐싱

Turborepo는 이미 계산한 것들은 다시는 계산하지 않는다는 핵심 컨셉 아래 캐싱이 이루어지고 있습니다. 이전에 실행한 파일 및 로그를 캐싱해, 만약 현재 실행 태스크에 이미 완료한 작업이 있다면 이것을 건너뛰기 때문에 작업 실행 시간을 효과적으로 줄일 수 있으며 파일의 내용에 따른 hash 정책을 사용합니다.

이 hash 값들은 아래 이미지와 같이 node_modules/.cache/turbo 하위에 hash로 구분된 스냅샷 폴더와 매칭됩니다.

 

Package Manager -  PnPm 

Turborepo는 종속성을 관리하는 역할을 하지 않으며, 해당 작업은 사용자가 선택한 패키지 관리자에게 맡겨집니다.

즉 올바른 외부 종속성 버전 다운로드, 심볼릭 링크, 모듈 확인 등의 작업은 패키지 관리자가 처리해야 합니다. 

 

저는 다양한 패키지매니저 중 모노레포에 적합하다고 생각되는 yarn berry와 pnpm을 고민했고 아래와 같은 이유로 pnpm을 선정했습니다.

  • vite, turborepo, nextjs, vue 등 이미 거대한 프로젝트들이 pnpm을 도입했다는 안전성
  • yarn berry 사용자들이 느낀 호이스팅의 불편함, yarn workspace의 자잘한 버그들
  • 현재 개발팀원들이 가지는 pnpm환경에 대한 비교적 익숙함
  • 기본적으로 workspace를 지원하고 그것이 공식문서에 잘 정의되어있음

등을 토대로 pnpm을 선택했습니다. 모노레포를 도입하면서 최대한 이러한 과정이 개발 병목으로 이어지지 않는 것을 가장 중점적으로 고려했던 것 같습니다.

해서 아래 기본 셋업을 통해 모노레포 패키지를 만들면

npx create-turbo@latest

아래와 같은 구조로 패키지가 세팅이되는데 저는 여기서 필요없는 doc을 지워주고 ui는 디자인시스템 패키지로 활용하였습니다.

repo/
├── apps/
│   ├── web/      
│   └── doc/  
├── packages/
│   ├── ui/   
│   ├── tailwind-config/ 
│   ├── eslint-config/ 
│   └── typescript-config/

그 후 .vscode/code-workspace에 아래와 같은 세팅으로 vscode에서 특정 영역레포를 접근하기 조금 더 용이하도록 아래 코드를 추가합니다.

// monorepo 프로젝트 폴더 구조
{
  "folders": [
    {
      "name": "web",
      "path": "../apps/web",
    },
    {
      "name": "ui",
      "path": "../packages/ui",
    },
  ],
}

workspace 적용후 폴더구조

 

이렇게 세팅후 아래 명렁어로 build를 돌려보면 이미지와 같은 결과물을 확인할 수 있습니다.

pnpm turbo run build

그리고 다시 한번 진행했을 때는 아래처럼 1Cached FULL TURBO 메세지를 확인할 수 있는데 이는 위에서 언급한 turborepo의 "동일한 작업을 두번 수행하지 않는다" 는 캐싱전략 때문입니다. 

이러한 캐싱전략은 로컬에서 작업할 때 상당한 시간을 절약해주고 원격 캐싱을 활용하여 전체 팀 CI에서 캐시를 공유하면 더 큰 효과를 볼 수 있다고 합니다.

https://turborepo.com/docs/crafting-your-repository/caching

 

 

이렇게 간단한 모노레포 세팅을 마쳤고 기존에 존재하던 packages에 eslint와 typescript 세팅을 진행하면 됩니다. 추후 tailwind와 디자인시스템 적용기, CI머신 원격 캐시 도입기로 돌아오겠습니다.

//의존성 방향
Apps (web, admin) 
    ↓ depends on
UI Components 

 

 

Reference

 

 

 

'Front-End' 카테고리의 다른 글

React query 쿼리키 관리하기 (feat: query key factory)  (0) 2024.08.27
Next.js톺아보기 What How When Why  (10) 2023.11.29
cookie  (0) 2023.11.15
[패키지 매니저] npm, yarn, pnpm, yarn-berry  (0) 2023.04.14
IE에서 ajax cache이슈  (0) 2022.05.11
Comments