Clean Architecture

모노레포 인사이트: Nx, Turborepo 그리고 PNPM

개발자엄지희 2025. 5. 26. 15:16
반응형

소개

모노레포의 세계로 뛰어들어볼까요? Nx, PNPM, Turborepo 등 어떤 툴을 사용하는 것이 좋을까요?

여기서는 모노레포 매니저의 특징, 성능 및 특정 요구 사항에 대한 적합성을 평가하면서 모노레포 매니저에 대해 자세히 알아봅시다. 우리의 목표는 개발 프로세스를 개선하여, 워크플로우를 효율적으로 만들고 코드 베이스를 간소화하는 것입니다.

이번 입문편에서는 모노레포 매니저의 기반이 되는 이론적 개념을 살펴봄으로써 기초를 다져보겠습니다. 아래 목록을 자세히 살펴보겠습니다.

 

이제 안전벨트를 매고 출발합시다! 🚀 🌟


모노레포 매니저 및 빌드 시스템

모노레포 관리자와 빌드 시스템을 종종 혼동하는 경우가 있습니다. 그러나 이들은 모노레포 생태계 내에서 상호 보완적인 역할을 하는 별개의 개념입니다. 모노레포 개발을 최대한 활용하려면 이들의 관계를 이해하는 것이 중요합니다.

빌드 시스템이란?

빌드 시스템은 코드 베이스의 핵심입니다. 빌드 시스템은 일련의 자동화된 단계를 통해 원시 소스 코드를 실행할 수 있는 애플리케이션 또는 라이브러리로 변환합니다.

  • 1️⃣ 빌드 작업:
    • ✔️ 컴파일/트랜스파일: 한 언어(예: 타입스크립트)의 코드를 기계가 이해할 수 있는 다른 언어(예: 자바스크립트)로 변환하는 작업입니다.
    • ✔️ 코드 경량화: 공백 제거, 변수 이름 바꾸기 등의 기술을 통해 코드 크기를 줄여 로딩 시간을 개선합니다.
    • ✔️ 번들링: 코드와 자산(이미지, 글꼴 등)을 배포에 최적화된 파일로 패키징하는 작업입니다.
  • 2️⃣ 린팅 및 포멧팅: 일관된 코드 스타일과 품질 표준을 적용하여 유지보수를 용이하게 만듭니다.
  • 2️⃣ 테스트: 자동화된 테스트(단위, 통합, e2e)를 실행하여 기능을 검증하고 오류를 조기에 포착합니다.
    https://www.hongkiat.com/blog/webpack-introduction/

모노레포 매니저란?

반면에 모노레포 매니저는 비유하자면, 모노레포라는 복잡한 교향곡을 지휘하는 지휘자입니다. 이들은 다음과 같은 도구와 구조를 제공합니다.

  • ✔️ 의존 관리: 모노레포 내에서 프로젝트 간의 의존을 효율적으로 해결하고 연결하여 원활한 협업을 보장하고 충돌을 방지합니다.
  • ✔️ 작업 오케스트레이션: 여러 프로젝트에서 빌드, 테스트 및 기타 작업의 실행을 조율하여 종속성을 고려하고 병렬 처리를 통해 최적화를 달성합니다.
  • ✔️ 코드 공유: 모노레포의 여러 프로젝트에서 코드와 구성 요소를 재사용할 수 있도록 하여 일관성을 높이고 중복성을 줄입니다.
  • ✔️ 도구 통합: 선호하는 개발 도구(린터, 포맷터, 테스트 러너)와 원활하게 통합하여 통합된 환경을 제공합니다.
  • ✔️ 워크플로 및 컨벤션: 프로젝트 구조, 이름 지정 및 작업 실행을 위한 규칙을 수립하여 일관성과 유지 관리의 용이성을 높입니다.

https://monorepo.tools/


강력한 시너지 효과

모노레포 관리자는 빌드 시스템의 기능을 활용하여 코드를 아티팩트로 실제 변환하는 작업을 수행합니다. 하지만 모노레포 관리자는 이를 넘어 모노레포 관리를 간소화하는 추상화 계층을 제공합니다.

빌드 시스템을 자동차의 엔진이라고 가정한다면 모노레포 관리자는 운전자라고 생각하면 됩니다. 엔진은 동력을 제공하지만, 운전자는 엔진이 원활하고 효율적으로 주행할 수 있도록 지시합니다.

예를 들면 아래와 같은 것들입니다.

✔️ Nx: 강력한 태스크 러너를 사용하여 빌드와 테스트를 오케스트레이션하는 동시에 모노레포에 특화된 다른 기능도 제공합니다.
✔️ Turborepo: 고급 캐싱 및 태스크 파이프라이닝을 통해 기존 빌드 도구(예: npm 스크립트)의 실행을 최적화합니다.
✔️ pnpm workspace: 기존 빌드 스크립트와 원활하게 통합하면서 효율적인 의존성 관리에 중점을 둡니다.


모노레포의 과제 극복하기

모노레포에는 빌드 시스템과 모노레포 관리자 모두에게 신중한 솔루션이 필요한 고유한 과제가 있습니다. 이러한 주요 과제와 최신 도구가 과제를 어떻게 해결했는지 자세히 알아봅시다.

1️⃣ 순환 의존성 풀기:

 

https://www.researchgate.net/figure/Circular-dependency-chain_fig5_4283963

🔳 문제: 순환 종속성은 하나의 모노레포 내에서 두 개 이상의 프로젝트가 서로 의존하여 폐쇄 루프를 만들 때 발생합니다. 이에 따라 빌드 실패, 무한 루프 및 개발 워크플로우의 전반적인 혼란이 발생할 수 있습니다.


2️⃣ 개발자 경험(DX) 향상:

🔳 문제: 열악한 개발자 경험(DX)은 생산성을 저해하고 불만을 초래할 수 있습니다. 느린 빌드, 이해하기 어려운 오류 메시지, 복잡한 툴링이 일반적인 원인입니다.

🔳 솔루션: 효과적인 빌드 시스템과 모노레포 관리자는 다음을 제공하여 DX를 우선시합니다.
✔️ 명확한 오류 메시지: 오류의 정확한 원인을 정확히 파악하여 실행할 수 있는 제안을 제공합니다.
✔️ 직관적인 인터페이스: 간소화된 워크플로우를 위한 사용자 친화적인 명령줄 인터페이스(CLI), 시각적 도구(예: Nx Console) 또는 IDE 통합을 제공합니다.
✔️ 빠른 피드백 루프: 증분 빌드 및 캐싱을 활용하여 코드 변경 사항에 대한 신속한 피드백을 제공합니다.
✔️ 핫 모듈 리로딩(HMR): 전체 페이지를 다시 로드하지 않고도 실행 중인 애플리케이션을 즉시 업데이트할 수 있어 개발 주기를 더욱 개선할 수 있습니다.


3️⃣ 프로젝트 성장에 따른 확장:

https://www.drawio.com/blog/dependency-graphs

🔳 문제: 모노레포의 규모와 복잡성이 증가함에 따라 의존성 관리, 작업 오케스트레이션, 빌드 성능 보장이 점점 더 어려워지고 있습니다.

🔳 솔루션: 강력한 빌드 시스템과 모노레포 관리자는 이러한 확장 문제를 해결할 수 있는 기능을 제공합니다.

✔️ 지능형 작업 스케줄링: 빌드 순서를 최적화하고 독립적인 작업을 병렬화하여 빌드 시간을 단축합니다.
✔️ 고급 캐싱: 중복 작업을 방지하기 위해 중간 빌드 아티팩트를 저장합니다.
✔️ 분산 빌드: 대규모 프로젝트를 위해 여러 머신에 빌드를 배포합니다.
✔️ 코드 분할: 대규모 애플리케이션을 더 작은 청크로 분할하여 빌드 속도를 높이고 성능을 개선합니다.


4️⃣ 팀 협업 강화:

🔳 문제: 대규모 팀에서는 개발 작업을 조정하고, 공유 종속성을 관리하고, 충돌을 피하는 것이 로직의 악몽이 될 수 있습니다.

🔳 솔루션: 최신 도구는 팀을 다음과 같이 지원합니다.

✔️ 세분화된 의존성 관리: 프로젝트가 공유 라이브러리의 특정 버전에 종속되도록 허용하여 변경 사항이 중단되는 것을 방지할 수 있습니다.
✔️ 병렬 개발: 개발자가 서로의 발목을 잡지 않고 모노레포의 여러 부분을 동시에 작업할 수 있습니다.
✔️ 코드 리뷰 워크플로우: 코드 리뷰 도구와 통합하여 코드 품질과 일관성을 보장합니다.
✔️ 릴리스 관리: 여러 프로젝트를 게시하고 배포하는 프로세스를 간소화합니다.

이제 모노레포 개발의 일반적인 장애물을 살펴봤으니 모노레포 관리자가 종속성을 효율적으로 관리하는 방법에 대해 자세히 알아보겠습니다. 🌟


모노레포의 의존성 관리: 그래프 기반 접근 방식

모노레포에서 종속성을 관리하는 것은 거대한 실타래를 푸는 것처럼 느껴질 수 있으며, 순환 의존성, 버전 충돌, 느린 빌드 시간으로 이어질 수 있습니다. 하지만 더 나은 방법이 있다면 어떨까요?

이 섹션에서는 그래프 이론이 모노레포의 의존성 구조를 시각화, 분석 및 최적화하여 보다 효율적이고 즐거운 개발 환경을 만드는 데 어떻게 도움이 되는지 살펴볼 것입니다. 시작하겠습니다! ✈️


유향 비순환 그래프(DAG)

🔳 많은 모노레포 관리자의 핵심에는 그래프 이론의 강력한 도구인 유향 비순환 그래프(DAG)가 있습니다. 이 구조는 프로젝트 간의 복잡한 의존성 웹을 우아하게 표현하여 원활하고 효율적인 개발 프로세스를 보장합니다.

🔳 앞서 언급한 정의는 DAG의 두 가지 주요 특징을 강조합니다.

✔️ 유향: 그래프의 가장자리에는 방향이 있어 한 프로젝트가 다른 프로젝트에 의존하고 있음을 나타냅니다. 프로젝트 A가 프로젝트 B에 종속된 경우 노드 A에서 노드 B를 가리키는 화살표(엣지)가 있습니다.

✔️ 비순환: 그래프에 주기가 포함되어 있지 않습니다. 즉, 한 노드에서 시작하여 가장자리를 따라 경로를 따라가다가 다시 같은 시작 노드로 돌아올 수 없습니다. 프로젝트 측면에서 보면 순환 종속성이 존재하지 않습니다(예: 프로젝트 A가 B에 종속되고, B가 C에 종속되며, C가 A에 종속되는 경우).

https://www.researchgate.net/figure/A-directed-acyclic-graph-DAG-representing-a-possible-causal-model-for-the-underlying_fig1_369413899

🔳 유향 비순환 그래프(DAG)는 특정한 특성으로 인해 여러 가지 이유로 유용합니다.

✔️ 의존성 표현: DAG는 개체 또는 이벤트 간의 종속성을 표현하는 데 탁월합니다. 어떤 항목이 어떤 다른 항목에 종속되어 있는지를 지시된 가장자리를 통해 명확하게 보여 주며, 비순환적 특성으로 인해 충돌이나 무한 루프로 이어질 수 있는 순환 종속성이 없습니다.

✔️ 워크플로 및 프로세스 모델링: DAG는 작업이 서로 특정 종속성을 갖는 복잡한 워크플로우와 프로세스를 모델링할 수 있습니다. 이를 통해 작업이 올바른 순서로 실행되고 리소스를 효율적으로 활용할 수 있습니다.

✔️ 데이터 처리 및 스케줄링: 데이터 처리 파이프라인에서 DAG는 데이터 변환 및 계산의 흐름을 표현하여 각 단계가 종속성이 충족된 후에 실행되도록 할 수 있습니다. 또한 스케줄링 시스템에서 종속성과 우선순위에 따라 작업을 실행할 순서를 결정하는 데도 사용됩니다.

✔️ 버전 관리 및 기록: DAG는 Git과 같은 버전 관리 시스템에서 코드 변경 내역을 추적하는 데 사용됩니다. 각 커밋은 DAG의 노드이며, 간선은 커밋 간의 부모-자식 관계를 나타냅니다.

✔️ 빌드 시스템: Gradle, Bazel과 같은 빌드 도구는 DAG를 사용하여 소스 코드 파일을 컴파일하고 링크할 순서를 결정하여 종속성이 필요하기 전에 빌드되도록 합니다.

🔳 DAG를 효과적으로 탐색하고 구성하기 위해 특정 알고리즘이 사용됩니다.

✔️ 위상 정렬은 정점을 방문할 수 있는 순서를 결정하여 DAG를 선형화하는 핵심 알고리즘으로, 정점 u에서 v까지의 모든 방향 엣지에 대해 정점 u가 v 앞에 오도록 합니다. 이 순서를 통해 작업이 실행되기 전에 종속성이 충족되도록 보장합니다.

✔️ 깊이 우선 탐색(DFS)  너비 우선 탐색(BFS) 과 같은 다른 알고리즘도 DAG를 트래버스하는 데 사용할 수 있습니다. DFS는 하나의 경로를 최대한 깊게 탐색한 후 역추적하는 반면, BFS는 한 정점의 바로 옆 이웃을 모두 방문한 후 그 다음으로 이동합니다. 이러한 알고리즘은 사이클 탐지 또는 주어진 시작점에서 도달할 수 있는 모든 노드를 찾는 것과 같은 작업에 유용합니다.

DAG는 관계를 표현하는 데 탁월하지만, 작업 그래프는 구조를 넘어 모노레포에서 일어나야 하는 작업을 포착합니다. 그 방법을 살펴보겠습니다. 🚂


작업 그래프

유향 비순환 그래프(DAG)는 모노레포에서 패키지 간의 정적 종속성을 효과적으로 표현하지만, 작업 그래프는 한 단계 더 나아갑니다. 작업 그래프는 코드를 빌드, 테스트 및 배포하는 데 필요한 동적 작업 워크플로우를 모델링합니다.

🔳 작업 그래프는 다음과 같은 특수한 유형의 DAG입니다.

✔️ 노드: 파일 컴파일, 특정 패키지에 대한 테스트 실행 또는 서비스 배포와 같은 개별 작업 또는 작업을 나타냅니다.

✔️ 엣지: 작업 간의 종속성을 정의하여 다른 작업을 시작하기 전에 완료해야 하는 작업을 나타냅니다. 작업 B를 시작하기 전에 작업 A를 완료해야 하는 경우, 엣지는 A에서 B를 가리킵니다.

https://nlguillemot.wordpress.com/2017/01/13/using-cont-with-tbbtask_group/

모노레포 관리자는 작업 그래프를 사용하여 복잡한 워크플로를 오케스트레이션합니다. 일반적으로 다음과 같이 작동합니다.

✔️ 그래프 생성: 명령(예: nx build myapp)을 실행하면 모노레포 매니저가 프로젝트의 구성과 소스 코드를 분석하여 필요한 작업과 종속성을 결정합니다. 이 정보는 작업 그래프를 구성하는 데 사용됩니다.

✔️ 위상 정렬: 그런 다음 관리자는 작업 그래프에 위상 정렬(나중에 살펴보겠습니다.)을 적용하여 작업을 실행할 수 있는 유효한 순서를 설정합니다. 이렇게 하면 작업이 시작되기 전에 종속성이 충족됩니다.

✔️ 작업 실행: 관리자는 위상적으로 정렬된 순서대로 작업을 실행합니다. 종종 독립적인 작업을 병렬로 실행하여 프로세스 속도를 크게 높일 수 있습니다.

✔️ 캐싱: 많은 모노레포 관리자는 반복 작업을 피하려고 캐싱을 활용합니다. 작업의 입력(소스 코드, 의존성)이 변경되지 않은 경우 캐시된 출력을 재사용할 수 있습니다.

🔵 두 개의 프로젝트가 있는 단순화된 모노레포를 고려해 보겠습니다.

  • ui-components: UI 컴포넌트 라이브러리
  • my-app: ui-components를 사용하는 애플리케이션

my-app 빌드를 위한 작업 그래프는 다음과 같습니다.

build(ui-components) --> lint(ui-components) --> test(ui-components) --> build(my-app) --> lint(my-app) --> test(my-app)

DAG와 작업 그래프는 종속성을 우아하게 모델링하지만, 본질적으로 명확한 실행 순서를 제공하지는 않습니다. 이러한 복잡한 구조를 모노레포 빌드 프로세스를 안내하는 실행할 수 있는 시퀀스로 변환하는 위상 정렬이 필요한 이유입니다. 한번 보시죠! 🚁


위상 정렬

🔳 유향 비순환 그래프(DAG) 영역에서 위상 정렬은 추상적인 의존성 웹을 구체적이고 실행할 수 있는 시퀀스로 변환하는 중추적인 알고리즘입니다.

https://www.naukri.com/code360/library/topological-sorting

🔳 보시다시피, 동일한 유향 비순환 그래프(DAG)에 대해 여러 가지 유효한 위상 순서가 있을 수 있습니다. 모노레포 관리의 맥락에서 이는 패키지에 대해 유효한 빌드 순서가 여러 개 있을 수 있음을 의미합니다. 선택된 특정 순서는 다음과 같은 요인에 따라 달라질 수 있습니다:

✔️ 병렬성: 일부 빌드 도구는 작업의 병렬 실행 가능성을 극대화하는 순서를 우선순위로 지정할 수 있습니다.

✔️ 최적화: 빌드 시간이나 리소스 사용량을 최소화하기 위해 선택한 순서가 최적화될 수 있습니다.

✔️ 사용자 지정: 일부 도구에서는 위상 정렬 알고리즘의 출력에 영향을 주는 기본 설정이나 제약 조건을 지정할 수 있습니다.

💡 DAG에 대해 여러 가지 유효한 위상 순서를 지정할 수 있는 기능은 일종의 적응형 관리로 비유할 수 있습니다. 머신러닝(ML) 모델이 데이터를 기반으로 동작을 학습하고 적응하는 것처럼, 모노레포 관리자는 다양한 위상 순서를 활용하여 빌드 프로세스를 최적화하거나 변화하는 프로젝트 요구 사항에 대응할 수 있습니다.

또한 모노레포의 맥락에서 위상 정렬은 매우 중요합니다.

✔️ 빌드 순서: 종속성에 따라 패키지를 빌드하거나 컴파일할 올바른 순서를 결정합니다.

✔️ 작업 실행: 작업 간의 종속성을 고려하여 작업(예: 린팅, 테스트, 배포)을 스케줄링합니다.

✔️ 의존성 분석: 순환 의존성(위상 정렬을 불가능하게 만드는)과 같은 잠재적인 문제를 식별합니다.

✔️ 캐싱: 종속성이 변경될 때만 패키지가 다시 빌드되도록 하여 빌드 캐시를 최적화합니다.

위상 정렬은 의존성 관계를 이해하는 데 탄탄한 기반을 제공하지만, 모노레포 관리자는 작업과 관련 메타데이터의 복잡한 상호 작용을 표현하기 위해 보다 전문적인 그래프 구조를 사용하는 경우가 많습니다.

🔵 예시: 다음과 같은 프로젝트와 종속성이 있는 모노레포 작업 공간을 상상해 보세요.

apps:

  • store-ui(storefront 웹 애플리케이션)
  • admin-ui(관리자 대시보드 웹 애플리케이션)

libs:

  • shared-ui(두 애플리케이션에서 공유하는 UI 컴포넌트)
  • product-data-access(제품에 대한 데이터 가져오기 로직)
  • cart-data-access(장바구니용 데이터 가져오기 로직)
  • auth(인증 라이브러리)
  • utils(유틸리티 함수)

이러한 프로젝트 간의 종속성은 다음과 같이 DAG에서 시각화할 수 있습니다.

store-ui --> shared-ui
store-ui --> product-data-access
store-ui --> cart-data-access
store-ui --> auth
admin-ui --> shared-ui
admin-ui --> auth
shared-ui --> utils
product-data-access --> utils
cart-data-access --> utils
auth --> utils

https://velog.io/@tap_kim/translate-monorepo-insights-nx-turborepo-and-pnpm

 

(번역) 모노레포 인사이트: Nx, Turborepo 그리고 PNPM

이 시리즈에서는 모노레포 매니저의 특징, 성능 및 특정 요구 사항에 대한 적합성을 평가하면서 모노레포 매니저의 세계에 대해 자세히 알아볼 것입니다.

velog.io

 

반응형