모노레포 vs 멀티레포: 어떻게 선택할까? (실무에서 써본 후기)

2025. 11. 7. 19:10·Dev

개발을 시작할 때 제일 먼저 고민하는 것 중 하나가 바로 프로젝트 구조입니다.

코드가 커질수록 "이걸 한 저장소에 다 넣어아 하나?" "아니면 프로젝트별로 따로 관리해야 하나?" 하는 고민이 생깁니다.

 

이 글에서는 그런 고민을 정리해보려 합니다.


1. 멀티레포에서 시작한 이유

처음에는 각 프론트엔드 앱을 따로 관리하는 멀티레포 구조를 선택했습니다.

각 앱이 독립적으로 배포되어야 했고, 기능도 달랐기 때문입니다.

 

하지만 곧 여러 문제가 발생했습니다.

 

공통 UI 컴포넌트의 중복 관리

project1/src/components/Button.tsx
project2/src/components/Button.tsx
project3/src/components/Button.tsx
  • 같은 Button, Modal, Table 등의 컴포넌트를 모든 저장소에 복사
  • 수정할 때마다 3~4개 저장소를 전부 업데이트

→ 한 곳에서 관리할 수 없다는 점이 큰 비효율이었습니다.

 

스타일 시스템 불일치

공통 색상, 폰트, 간격, spacing 등이 각 저장소에 제각각 정의되어 있었습니다.

  • 버튼 색을 바꿔야할 때 4개 저장소 모두 수정
  • 디자인 토큰이 일관되지 않아 UI 차이 발생

→ 스타일의 일관성 유지가 거의 불가능했습니다.

 

타입, 상수, 유틸 함수 중복

// 공통 API 타입 정의
type ApiResponse<T> = {
  data: T;
  message: string;
  status: number;
}
  • API 타입, 유틸 함수, 상수를 복사해 각 저장소에 똑같이 복붙
  • 수정 시 모든 저장소를 동기화해야 함

→ 코드 일관성 유지가 어려웠습니다.

 

의존성 버전 혼란

  • React, TypeScript, Vite 버전이 앱마다 달라짐
  • 한쪽에서 업데이트하면 다른 앱에서 빌드 에러 발생
  • 보안 패치 적용 시 모든 저장소를 일일이 수정해야 함

2. 모노레포로 전환

위 문제를 해결하기 위해 pnpm workspace 기반 모노레포 구조로 전환하였습니다.

 

✅ 프로젝트 구조 예시

project/ 
├── apps/ 
│ ├── project1/ 
│ ├── project2/ 
│ └── project3/ 
├── packages/ 
│ ├── common_ui/ 
│ ├── common_utils/ 
│ ├── common_constants/ 
│ ├── common_styles/ 
│ └── common_types/ 
└── package.json
  • apps/: 실제로 배포되는 서비스들
  • packages/: 여러 앱에서 공통으로 사용하는 패키지들

 

✅ 루트 설정 (pnpm workspace)

// package.json
{
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ]
}

 

✅ 공통 패키지 예시

// packages/common_ui/src/components/Button.tsx
export const Button = ({ size: 'medium', children }) => {
  return (
    <button className={`btn btn-${size}`}>
      {children}
    </button>
  );
};

 

✅ 공통 타입 정의 공유

// packages/common_types/src/types/CommonTypes.ts
export type ApiResponse<T> = {
  data: T;
  message: string;
  status: number;
};

// apps/project1/src/api/useUser.ts
import { ApiResponse } from '@project/common_types';

export const getUser = async (): Promise<ApiResponse<User>> => {
  // ...
};

 

✅ 통합 빌드 및 테스트

// 루트 package.json
{ 
  "scripts": { 
    "build": "pnpm --parallel build", 
    "test": "pnpm --recursive test", 
    "project1:build": "pnpm --filter @project/project1 build", 
    "common:build": "pnpm --filter @project/common_ui build" 
  } 
}
  • 필요한 앱만 선택 빌드 가능, 전체 빌드도 한번에 가능

 

✅ 실제 사용 예시

// apps/project1/src/components/SomeComponent.tsx 
import { Button } from '@project/common_ui'; 
import { formatDate } from '@project/common_utils'; 
import { DATE_FORMATS } from '@project/common_constants'; 
import '@project/common_styles'; 

const MyComponent = () => { 
  const date = formatDate(new Date(), DATE_FORMATS.YYYY_MM_DD); 
  return <Button size="medium">{date}</Button>; 
};
  • 모든 앱이 동일한 인터페이스와 스타일로 동작함

3. 도입 후 얻은 효과 및 결론

항목 도입 전 (멀티레포) 도입 후 (모노레포)
공통 코드 관리 중복, 수동 반영 한 곳에서 수정 즉시 반영
디자인 시스템 앱마다 불일치 통일된 스타일 시스템
타입 정의 복사-붙여넣기 패키지로 공유
의존성 관리 버전 충돌 잦음 단일 버전 유지
빌드/테스트 개별 실행 통합 및 선택적 실행
협업 각자 다른 저장소 하나의 환경에서 공동 개발

 

결론

1. 앱 간 공통 코드 공유가 필수였다.

2. 디자인 시스템을 일관되게 유지해야 했다.

3. 모든 앱을 같은 팀이 관리했다.

 

이런 조건이라면 모노레포가 훨씬 효율적입니다.

반대로 팀이 나뉘어 있고 기술 스택이 다르면 멀티레포가 더 나을 수 있습니다.


참고

https://velog.io/@miso1489/%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%AC%EC%99%80-%EB%A9%80%ED%8B%B0%EB%A0%88%ED%8F%AC

'Dev' 카테고리의 다른 글

Git 에러: POST git-receive-pack (chunked), send-pack: unexpected disconnect while reading sideband packet, fatal: the remote end hung up unexpectedly, Everything up-to-date  (0) 2025.11.07
프론트엔드 디렉토리 정리: Barrel Export (배럴 파일 구조) (+ 사용 시 주의점)  (0) 2025.05.13
직접 써본 FSD (Feature-Sliced Design) 적용기: 깔끔함과 혼란 사이  (0) 2025.04.05
[OAuth] OAuth 2.0을 파헤쳐보자 🔍 + 소셜로그인 예제  (0) 2025.03.05
[OAuth] GCP (Google Cloud Platform)에서 Client ID 생성하기 🪪  (0) 2025.03.05
'Dev' 카테고리의 다른 글
  • Git 에러: POST git-receive-pack (chunked), send-pack: unexpected disconnect while reading sideband packet, fatal: the remote end hung up unexpectedly, Everything up-to-date
  • 프론트엔드 디렉토리 정리: Barrel Export (배럴 파일 구조) (+ 사용 시 주의점)
  • 직접 써본 FSD (Feature-Sliced Design) 적용기: 깔끔함과 혼란 사이
  • [OAuth] OAuth 2.0을 파헤쳐보자 🔍 + 소셜로그인 예제
Lucy96
Lucy96
개발새발 프론트엔드 개발자
  • Lucy96
    Lucy dev ✨
    Lucy96
  • 전체
    오늘
    어제
    • 분류 전체보기 (21)
      • JavaScript (3)
      • React (2)
      • HTTP (1)
      • GIS (1)
      • 회고 (3)
      • Dev (9)
      • CSS (1)
      • DB (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    sessionStorage
    프론트엔드
    소셜로그인
    oauth
    토스모닥불
    react
    Hoisting
    자바스크립트엔진
    토스
    HTTP
    geojson
    Cookie
    BEM
    cors
    scss
    cliend id
    CSS
    회고
    gcp
    Google Cloud Platform
    JavaScript
    콜백큐
    OAuth 2.0
    Mapbox
    논블로킹
    webapis
    이벤트루프
    localStorage
    scope
    블로킹
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Lucy96
모노레포 vs 멀티레포: 어떻게 선택할까? (실무에서 써본 후기)
상단으로

티스토리툴바