development

고성능 모노레포 구축하기: Turborepo와 pnpm의 환상적인 조합

들어가며

서비스 규모가 커지면 필연적으로 여러 개의 프로젝트(웹, 관리자 페이지, 모바일 API 등)를 운영하게 됩니다. 이때 각기 다른 저장소에서 공통 로직을 복사 붙여넣기하며 관리하는 것은 재앙에 가깝죠. 그래서 등장한 것이 **모노레포(Monorepo)**입니다.

하지만 모노레포도 관리가 부실하면 빌드 속도가 기하급수적으로 느려집니다. 오늘은 2025년 기준 가장 강력한 조합인 Turborepopnpm을 이용해 빠르고 효율적인 모노레포 환경을 구축하는 방법을 알아보겠습니다.

1. 왜 pnpm과 Turborepo인가?

많은 도구(Lerna, Nx 등)가 있지만, 제가 이 조합을 추천하는 이유는 명확합니다.

  • pnpm: ‘Content-addressable’ 저장소 방식을 사용하여 디스크 공간을 절약하고, 의존성 설치 속도가 압도적으로 빠릅니다. 모노레포의 workspace 기능을 가장 완벽하게 지원합니다.
  • Turborepo: Go 언어로 작성된 초고속 빌드 시스템입니다. ‘캐싱’과 ‘병렬 실행’을 통해 한 번 빌드한 코드는 다시 빌드하지 않는 지능적인 워크플로우를 제공합니다.

2. pnpm 워크스페이스 설정

먼저 프로젝트 루트에 pnpm-workspace.yaml 파일을 생성하여 구조를 정의합니다.

packages:
  - 'apps/*'      # 서비스 앱 (Next.js, Astro 등)
  - 'packages/*'  # 공통 라이브러리 (UI 컴포넌트, 유틸리티, 공유 설정)

이렇게 설정하면 packages/ui에서 만든 컴포넌트를 apps/web에서 로컬 패키지처럼 불러와 사용할 수 있습니다.

3. Turborepo로 빌드 파이프라인 최적화

Turborepo의 핵심은 turbo.json 설정입니다. 프로젝트 간의 의존성 관계를 정의하고 캐싱 전략을 세웁니다.

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"], // 의존성 패키지가 먼저 빌드되어야 함
      "outputs": [".next/**", "dist/**"] // 빌드 결과물을 캐싱함
    },
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

특히 dependsOn: ["^build"] 설정은 하위 라이브러리가 변경되었을 때만 관련 앱을 다시 빌드하게 만들어 빌드 시간을 극적으로 줄여줍니다.

4. 로컬 및 원격 캐싱 (Remote Caching)

Turborepo는 로컬 캐시뿐만 아니라 Remote Caching을 지원합니다. 내가 빌드한 결과를 팀원이나 CI 서버가 그대로 가져다 쓸 수 있다는 뜻입니다.

# Vercel과 연동하여 원격 캐싱 활성화
npx turbo login
npx turbo link

이제 CI/CD 파이프라인에서 “아무것도 변경되지 않은 패키지”는 0초 만에 빌드가 끝나는 마법을 경험하게 됩니다.

5. 모노레포 도입 시 고려할 점

  • 의존성 관리: 모든 패키지의 라이브러리 버전을 가급적 통일해야 충돌을 방지할 수 있습니다.
  • 코드 소유권: 프로젝트가 너무 커지면 CODEOWNERS 파일을 통해 각 패키지의 담당자를 명확히 하는 것이 좋습니다.

마치며

모노레포는 단순히 코드를 한곳에 모으는 것이 아니라, 공유와 협업의 효율을 극대화하는 전략입니다. Turborepo와 pnpm은 그 과정에서 발생하는 성능 저하라는 숙제를 완벽하게 해결해 줍니다.

작은 프로젝트라도 미래의 확장성을 고민하고 있다면, 오늘 소개한 모노레포 구성을 검토해 보세요. 개발 경험(DX)이 한 차원 높아질 것입니다!

이 글이 마음에 드셨나요?

로딩 중...