모놀리식에서 모노레포로, 그 사이의 과정들

이민영·2025년 3월 15일

모노레포

목록 보기
1/2
post-thumbnail

오늘은 회사 서비스 구조를 모놀리식에서 모노레포로 전환하는 과정에 있어 고민했던 과정들을 기록해보고자 한다.

현재 다니고 있는 회사에서는 다양한 서비스들이 운영되고 있다.
메인 서비스 5개, 세부 서비스 2개, 어드민 서비스 2개, 디자인 시스템 1개까지 합하면 총 10개의 서비스가 존재한다.

각 서비스는 서로 다른 기술 스택을 사용하고 있으며, 새로운 서비스에 투입될 때마다 최신 버전의 라이브러리를 설치했었다. 예를 들어, 어떤 서비스는 Next.js + TypeScript, 어떤 서비스는 React + TypeScript, 또 다른 서비스는 React + JavaScript, Vue + JavaScript 등 다양한 스택을 사용하고 있었다.

게다가, 모든 프로젝트가 하나의 레포지토리에 포함된 모놀리식 레포지토리(monolithic repository) 구조로 되어 있었다.

모놀리식 구조로 인해 불편했던 부분들

모놀리식 레포지토리란❓

모든 애플리케이션(=서비스)이 하나의 코드베이스에 통합되어, 백엔드, 프론트엔드, 데이터베이스 등 모든 구성 요소가 함께 배포되는 전통적인 방식이다. 주로 작은 규모의 애플리케이션에서 사용되며, 모든 코드를 단일 프로젝트로 관리하는걸 뜻한다.

초기에는 서비스 개수가 많지 않다 보니 큰 불편함이 없었다. 그러나 점점 서비스가 늘어나면서 여러 문제들이 나타나기 시작했다.

✅ 모놀리식 구조의 불편한 점

1️⃣ 패키지 버전 관리의 어려움

각 서비스마다 사용하는 라이브러리와 패키지의 버전이 다르게 설정되어 있다. 특정 서비스에서는 최신 버전을 사용하고, 다른 서비스에서는 구버전을 유지하는 식으로 일관성이 깨졌다.

2️⃣ 공통 UI 및 로직 중복 문제

여러 서비스에서 공통으로 사용하는 UI 컴포넌트나 로직이 있었지만, 각각의 서비스에 개별적으로 복사해서 사용해야 했다. 이는 유지보수를 어렵게 만들었고, 하나의 변경 사항을 여러 곳에서 반영해야 하는 비효율적인 구조를 초래했다.

3️⃣ 새로운 서비스 추가 시 환경 세팅의 번거로움

새로운 서비스가 추가될 때마다 프로젝트를 처음부터 설정해야 했다. 공통적인 설정이 존재하지 않아 매번 동일한 초기 작업을 반복해야 했고, 이는 개발 생산성을 떨어뜨렸다.

4️⃣ 소스 코드 관리의 복잡성

모든 서비스가 하나의 레포지토리에서 관리되다 보니, 각 서비스마다 별도의 브랜치를 생성해야 했다. 심지어, 그 브랜치 내에서도 dev와 main을 나눠야 했기 때문에 협업 시 소스 코드 관리를 효율적으로 하기 어려웠다. 브랜치 전략이 복잡해지고, 작은 변경 사항도 여러 서비스에 영향을 미칠 가능성이 높아졌다.


이러한 문제들이 누적되면서, 모놀리식 레포지토리의 한계를 절감하게 되었고, 이를 해결하기 위해 모노레포(Monorepo) 구조로의 전환을 고민하게 되었다.

멀티레포로 전환...

처음부터 모노레포로 전환하고 싶었지만, 당시 진행 중이던 프로젝트도 있어서 구조 변경에 많은 시간을 투자하기 어려웠다.
게다가, 기존 모놀리식 구조에서 각 서비스의 기술 스택이 제각각이라 이를 한꺼번에 분리하고 종속성을 정리하는 데는 꽤 오랜 시간이 걸릴 것이라고 판단했다.

그래서 우선, 모놀리식 구조에서 발생했던 문제점 중 소스 코드 관리의 복잡성을 먼저 해결하고자 했다.
이를 위해 멀티레포(Multi-Repo) 구조로 전환하는 것이 최선이라고 생각했다.

멀티 레포지토리란❓

멀티 레포지토리(Multi-Repo)란 각 서비스나 모듈을 별도의 레포지토리에서 개별적으로 관리하는 방식이다.
각 서비스는 독립적인 코드베이스를 가지며, 배포 및 종속성 관리도 각각 따로 이루어진다.

모놀리식 → 멀티레포 전환 과정

모놀리식에서 멀티레포로의 전환 자체는 오래 걸리지 않았다.
이미 기존 모놀리식 구조에서 각 서비스마다 종속성을 개별적으로 관리하고 있었기 때문에, 브랜치로 관리하던 서비스들을 각기 다른 레포지토리로 분리하는 방식으로 진행하면 됐다.

또한, 우리 회사에서는 CI/CD를 Jenkins(젠킨스) 를 통해 관리하고 있었는데,
각 서비스의 레포지토리에 별도의 Jenkins 파일을 두어 관리하는 방식으로 적용할 수 있었다.

✅ 멀티레포 전환 후의 변화

멀티레포로 전환한 후, 가장 큰 변화는 서비스별 소스 코드 관리가 훨씬 명확해졌다는 점이다.
각 서비스가 개별적인 레포지토리를 가지게 되면서,

  • 코드 변경 이력이 서비스별로 분리되어 가독성이 좋아졌다.
  • 브랜치 충돌이나 소스 코드 관리 이슈가 줄어들었다.

하지만 각 레포지토리가 독립적으로 관리되면서, 패키지 종속성 관리의 복잡성과 공통 UI 컴포넌트 및 로직의 공유 문제는 여전히 해결되지 않았다.
서비스마다 중복된 코드가 발생했고, 변경 사항을 반영할 때마다 각 레포지토리를 개별적으로 수정해야 하는 번거로움이 있었다.

결국, 유지보수에 드는 비용(=시간)이 점점 커지면서,
이러한 문제를 근본적으로 해결하기 위해 모노레포(Monorepo) 전환을 다시 고민하게 되었다.


모노레포 전환 전 고민했던 것들 🤔

모노레포로 전환하면 확실히 공통 코드 관리가 쉬워지겠지만, 구조를 통째로 바꾸는 작업이 만만치 않다는 점이 걸렸다.
서비스마다 독립적으로 운영되던 레포지토리를 하나로 합치는 과정에서 예상보다 많은 리소스가 필요할 것 같았고,
"모노레포가 아닌 다른 방법으로 위에서 언급한 불편함을 해결할 수는 없을까?" 라는 고민이 들었다.

특히, 기존 멀티레포 구조에서는

  • 패키지 종속성 버전이 일관되지 않음 → 서비스마다 사용하는 라이브러리 버전이 제각각이라 유지보수가 어려움
  • 공통 UI 컴포넌트 및 로직 공유가 어려움 → 각 레포지토리에 중복된 코드가 계속 생김

이 두 가지 문제가 가장 컸다.
그래서 다음과 같은 대안들을 고려해봤다.


📌 서브모듈 방식

스토리북으로 만들어놓은 공통 UI 컴포넌트나 공통 로직을 각 프로젝트에 서브모듈로 연결해서 사용하는 방식

✅ 장점

  • 가장 간단한 해결책이다.
  • 기존 레포지토리 구조를 변경할 필요가 없고, 패키지 배포 없이 Git 서브모듈만 등록하면 된다.

❌ 단점

  • 업데이트 반영이 번거롭다 → 공통 컴포넌트나 로직에 변경 사항이 생기면, 각 프로젝트에서 직접 서브모듈을 업데이트해야 한다.
  • 패키지 종속성 문제 해결 불가능 → 각 프로젝트의 패키지 버전이 다 제각각이라 종속성 문제를 해결하는 데 도움이 되지 않는다.
  • 초기 설정이 복잡할 수 있다 → 각 프로젝트에 서브모듈을 연결하는 과정이 다소 번거로울 수 있다.

📌 패키지로 배포하여 사내 라이브러리 개발

공통 UI 컴포넌트나 공통 로직을 private 패키지로 배포하여, 사내 라이브러리처럼 활용하는 방식

✅ 장점

  • 기존 레포지토리 구조를 변경할 필요가 없다.
  • 중앙 집중식으로 관리할 수 있어, 버전 관리가 용이하다.
  • 각 프로젝트에서 원하는 버전의 패키지를 쉽게 설치하거나 업데이트할 수 있다.

❌ 단점

  • 패키지 종속성 문제 해결이 어려움 → 각 프로젝트가 개별적으로 패키지를 업데이트해야 해서, 모든 서비스가 동일한 버전을 유지하기 어렵다.
  • 배포 및 관리의 자동화가 필요하다 → 컴포넌트 업데이트 후 패키지를 배포하는 과정을 자동화하거나, 수동으로 관리해야 한다.
  • 진입장벽이 높다. → 우리 팀에서는 아직 패키지를 배포해본 경험이 없어, 이를 처음 도입하는 데 시간이 필요할 것 같다.

💰 추가 비용 고려

  • NPM 패키지 (Private 기준) : 인당 매달 $7
  • GitHub 패키지 (Private 기준) : 500MB까지 무료, 그 이상은 유료

이런 대안들을 고민해봤지만, 여전히 패키지 종속성 문제와 공통 코드 관리의 번거로움이 해결되지 않았다.

  • 서브모듈 방식은 업데이트 반영이 번거롭고, 패키지 종속성 문제를 해결할 수 없었다.
  • 사내 라이브러리 배포 방식은 중앙 관리가 가능하지만, 모든 프로젝트의 패키지 버전을 통일하는 문제까지 해결하지는 못하고 추가적인 비용도 발생된다.

결국, 장기적으로 유지보수 비용을 줄이고, 패키지 종속성과 공통 코드 관리 문제를 동시에 해결하기 위해 모노레포 전환이 필요하다고 판단했다.


결국, 돌고 돌아 모노레포로‼️

다양한 해결책을 고민해봤지만, 장기적인 유지보수 효율성과 개발 생산성을 고려해
결국 모노레포(Monorepo)로 전환하기로 결정했다.

모노 레포지토리란❓

모노레포(Monorepo) 는 여러 개의 프로젝트(서비스)를 하나의 레포지토리에서 관리하는 방식을 말한다.
독립적인 애플리케이션이더라도 공통된 코드, 패키지, 설정 등을 쉽게 공유할 수 있어, 협업과 유지보수의 효율성이 높일 수 있다.


✅ 모노레포 도입 시 얻을 수 있는 이점들

1️⃣ 공통 코드(공통 UI, 유틸 함수 등)의 일관된 관리

🔹 별도의 패키지 배포 없이 같은 레포지토리 내에서 즉시 공유 가능
🔹 모든 서비스에서 동일한 버전의 공통 코드를 사용하므로, 불필요한 버전 충돌 발생 방지

기존 방식

  • 공통 UI/유틸을 각 프로젝트에 중복 추가로 인해 코드
  • 코드 수정 시 모든 프로젝트를 개별적으로 수정해 유지보수 비용 증가

모노레포 전환 후

  • 단일 코드베이스에서 직접 참조하여 코드 관리 가능
  • 공통 코드 수정 시 한 곳에서만 변경하면 모든 서비스에 자동 반영

2️⃣ 패키지 종속성 관리가 간편해짐

🔹 의존성(Dependencies)을 중앙에서 통합적으로 관리할 수 있음
🔹 패키지 업데이트 시 모든 서비스가 동일한 버전 유지 가능
🔹 중복 설치된 패키지를 줄여서 빌드 최적화

기존 방식

  • 프로젝트마다 종속성이 달라서 버전 충돌 및 호환성 문제가 자주 발생
  • 신규 패키지를 추가할 때 각 레포지토리에서 따로 설치해야 했음

모노레포 전환 후

  • 하나의 package.json에서 공통 의존성을 통합 관리
  • 모든 서비스에서 동일한 패키지 버전 사용 가능
  • 빌드 시 패키지를 공유하므로, 빌드 속도 최적화

3️⃣ 개발 환경 설정의 일관성 유지

🔹 서비스마다 일일이 환경을 설정할 필요 없이 동일한 도구와 설정을 적용 가능
🔹 신규 서비스 추가 시 복잡한 초기 환경 세팅 없이 빠르게 시작 가능

기존 방식

  • 프로젝트마다 ESLint, Prettier, Webpack/Vite 설정이 제각각
  • 신규 프로젝트를 만들 때 환경 설정을 처음부터 다시 해야 했음

모노레포 전환 후

  • 공통 설정을 공유하여 일관된 개발 환경 유지
  • 신규 프로젝트 추가 시 기존에 설정된 환경을 그대로 가져와 초기 세팅 없이 바로 개발 가능

모노레포로 전환! 🚀

이러한 장점들을 고려해 최종적으로 모노레포로 전환하기로 결정했고,
현재는 Turorepbo를 활용하여 모노레포 환경을 구축한 상태다.

Turborepo를 사용하면

  • 패키지 캐싱을 활용한 빌드 속도 최적화
  • 각 프로젝트 간 종속성 관리 자동화
  • 공통 패키지 및 설정 공유

등의 장점이 있어, 대규모 서비스에서도 효율적으로 활용할 수 있다.

모노레포 전환 과정과 트러블슈팅은 다음 포스팅에서!

오늘은 모노레포로 전환하기까지의 고민 과정을 정리한 포스팅이므로,
실제 모노레포로 전환하는 과정과 그 과정에서 겪었던 트러블슈팅은 이후 포스팅에서 자세히 다뤄보겠다. 😉

profile
Frontend Developer

0개의 댓글