모노레포 - 마이크로 아키텍처를 지향하며

meek·2023년 4월 20일
0

TIL

목록 보기
14/24

들어가며 ...

tving의 채용 공고인데 프론트엔드 공고에 우대사항에도 모노레포가 있다!

이번 달 말부터 원티드에 진행되는 원티드 프론트엔드 프리온보딩에서 주제가 모노레포이다.

그래서 스마트팜을 운영하고 있는 스타트업인 그린랩스에서도 모노레포를 사용하고 있어 그린랩스의 기술블로그에서 가져오게 되었다 😚


저장소 모델

소스코드를 위한 구조화 옵션의 대표적인 두가지는 모노레포와 멀티레포가 있다.

모노레포(Monorepo)

모노레포는 모든 프로젝트에 대한 모든 코드와 자산을 저장하는 단일 저장소를 말한다. 서드 파티나 라이브러리 또는 여러 다른 프로젝트와 기술로 구성될 수 있다. 모노레포는 하나의 저장소에 모든 것이 담겨 있다.

이 때문에 배포 주기저장소의 강력한 결합이 발생된다.

모노레포는 모놀리스 레포가 아니라 모듈러 레포에 가깝다고 볼 수 있다. 재사용 가능한 컴포넌트와 라이브러리는 분리되어 있다.

  • 👉 "모노레포"가 "모듈러 레포"에 가깝다고 하는 이유는, "모노레포"가 모든 프로젝트에 대한 모든 코드와 자산을 단일 저장소에 저장하고, 재사용 가능한 컴포넌트와 라이브러리를 분리하여 사용한다는 점에서 "모듈러 레포"와 유사하다고 볼 수 있기 때문!

서드파티

편하고 효율적인 개발을 위해, 플러그인이나 라이브러리 또는 프레임워크등을 사용하게 된다. 예로 프로그래밍 개발과 개발자 사이에 플러그인,라이브러리,프레임워크를 서드파티로 볼 수 있다. 이처럼 제 3자로써 중간다리 역할을 하는 것을 서드파티라고 한다.

'모놀리스 레포'와 '모듈러 레포'

"모놀리스 레포"와 "모듈러 레포"는 소프트웨어 개발에서 두 가지 다른 개발 방법론을 나타내는 용어이다.

  1. 모놀리스 레포 (Monolithic Repository)
    모놀리스 레포하나의 큰 저장소(repository)에 모든 소스 코드와 관련된 코드, 설정, 문서 등이 함께 저장되어 있는 개발 방법론입니다. 즉, 여러 개의 프로젝트나 모듈이 하나의 레포지토리에 모두 포함되어 있습니다. 이 방식에서는 소스 코드의 변경이나 배포가 한 번에 이루어지며, 개발자들이 동일한 레포지토리를 공유하므로 통합이 쉽고, 코드간의 의존성이 낮아집니다. 그러나 레포지토리가 커지면 코드 관리가 복잡해지고, 여러 개의 모듈이 함께 변경되는 경우가 많아질 수 있습니다.

  2. 모듈러 레포 (Modular Repository)
    모듈러 레포각각의 모듈이나 프로젝트별로 별도의 레포지토리를 가지는 개발 방법론입니다. 각각의 모듈이 독립적으로 개발, 테스트, 배포되며, 서로 독립적으로 관리됩니다. 이 방식에서는 레포지토리가 작아져 개별 모듈의 관리가 용이해지고, 독립적인 배포가 가능하므로 개발 속도와 유연성이 높아집니다. 그러나 모듈 간의 의존성이 높아져 통합이 복잡해질 수 있습니다.


멀티레포(Multirepo)

멀티레포는 여러 저장소를 말한다. 일반적으로 많이 선택되고 있는 모델이다. 독립된 애플리케이션이나 다른 프로젝트와 직접적인 종속성이 없는 속성을 가지고 있다. 대부분 팀별 또는 프로젝트 별로 나누어 멀티레포 방식으로 개발을 진행하고 있다.

대부분의 앱들은 멀티레포이고 혼자 프로젝트를 진행하는 경우 대부분 모놀리스로 만들게 된다. 오픈소스들은 각각의 저장소를 가진 멀티레포로 되어 있지만 모듈러 형태로 누구나 사용할 수 있다.

모놀리식(Monolithic) 서비스 아키텍처

모놀리식 서비스 아키텍처는 구성요소의 긴밀한 결합(tight coupling)으로 인하여 아키텍처가 단단해지고 애플리케이션은 분리할 수 없는 하나의 단위로서 업데이트가 필요하거나 새로운 기능이 추가될 때마다 어려움을 겪게 된다.

  • 👉 모놀리식(Monolithic) 서비스 아키텍처는 모든 컴포넌트가 단일 서비스로 구성되어 있는 아키텍처이기 때문!

일반적으로 소규모 또는 POC, MVP 구현의 경우 간단하고 빠르기 때문에 모놀리식으로 시작하는 경우가 많다.


PoC, MVP

모두 제품의 개발이나 출시를 위한 테스트 방법이라고 볼 수 있다.

  1. PoC(개념 증명, Proof of Concept)
    개념 증명(POC, Proof of Concept)은 (기존 시장에 없었던) 신기술을 도입하기 전에 이를 검증하기 위해 사용한다. 특정 방식이나 아이디어를 실체화하여 타당성을 증명하는 것을 의미한다.
  • 의미 : 새로 도입되는 기술이나 제품이 도입 목적에 부합되는지 검증
  • 검증 : 그 기술이 생각한대로 동작 돼? 도입해도 돼?
  • MVP와 차이점 : 실사용자인 고객이 사용/피드백 안함
  1. MVP(최소 실행가능 제품, Minimum Viable Product)
    최소 실행 가능 제품(Minimum Viable Product, MVP)은 고객에게 가치를 제공해야 하며, 고객 피드백을 받아 생존하기 위한 최소한의 노력을 들여 만든 기능(features)을 구현한 제품이다. 최소 실행 가능 제품(MVP)은 신제품 개발에서 학습의 영향을 강조하는 Lean Startup의 개념이다.

MVP를 최소한의 노력으로 고객에 대한 검증 된 정보를 최대한 수집 할 수있는 새로운 제품 버전으로 정의했다. 사람들이 제품과 관련하여 실제로 무엇을하는지 보는 것이 사람들에게 무엇을 원하는지 묻는 것보다 훨씬 더 신뢰할 수 있다.

  • 의미 : 출시 후 제품이 생존하기 위해 최소한의 노력(개발범위, 시간)으로 고객에 대한 검증 된 정보를 최대한 수집 할 수있는 제품 버전
  • 검증 : 고객에게 Value를 주는가? 고객이 원하는 것이 맞아? 가설대로 고객이 변화 돼?
  • PoC, Prototype과 차이점 : 실사용자인 고객이 사용/피드백 해야 함
  • Pilot과 차이점 : 성공/실패 확인 보다는 지속적인 실험/개선 목적
  • MVP는 최소한의 노력으로 고객에게 Value를 주며 가설을 검증하며 개선

마이크로(Micro) 서비스 아키텍처

애플리케이션이 명확한 경계와 책임이 있는 독립된 구성요소 집합으로 분할되는 접근 방식으로 가장 많이 채택된 소프트웨어 아키텍처 유형이다. 모듈성과 확장성 기능을 갖추고 있어 유지 관리 및 테스트가 더 쉽다.

애플리케이션 개발을 위한 아키텍처 스타일을 의미한다. 주로 마이크로서비스라고도 하며, 마이크로 서비스 아키텍처를 사용하면 대규모 애플리케이션을 각각 담당 영역을 가진 소규모의 독립적인 구성요소로 구분할 수 있다. 마이크로서비스 기반 애플리케이션은 단일 사용자 요청을 처리하기 위해 여러 내부 마이크로서비스를 호출하여 응답을 작성할 수 있다.

대표적인 마이크로 서비스 아키텍처의 예시로 컨테이너가 있다.


모놀리스(Monolith) 프론트엔드 아키텍처

대부분의 프론트엔드 애플리케이션은 모놀리스이다.

단일 레이어 구조의 아키텍처를 가지고 있다. 최근에는 동형 아키텍처에 따라 비즈니스 로직이 백엔드가 아니라 프론트엔드에도 있다. 그 결과 애플리케이션이 복잡해지고 모놀리스 프론트엔드 애플리케이션이 되어간다.

마이크로(Micro) 프론트엔드 아키텍처

아래 그림은 마이크로 프론트엔드 애플리케이션의 일반적인 참조 아키텍처 중 하나이다.

마이크로 프론트엔드 아키텍처를 통해 복잡한 UI 애플리케이션을 일관된 사용자 경험을 유지하면서 더 작고 관리하기 쉬운 조각으로 나눌 수 있다. 그럼으로써 더 빠른 개발과 더 작은 청크(chunk)를 생성해 성능을 향상시킬 수도 있다. 또한 팀이 독립적으로 작업할 수 있게함으로서 결합을 줄이고 종속성을 최소화할 수 있다.

가장 대표적인 예시로는 구글 서비스가 있다.

구글의 메일, 드라이브, 캘린더 등 서비스들은 디자인과 기능을 통합시켰지만 여러 서브도메인에 걸쳐서 독립적인 앱으로서 실행되고 있다.

또 다른 예로 오늘의 집이 있다. 오늘의 집에는 홈 커뮤니티, 베스트, 셀프 가이드 등 여러 앱 서비스 카테고리가 있다. 서로 다른 도메인이지만 같은 페이지간 이동하는 것으로 착각할 수 있다.

마이크로 프론트엔드 아키텍처의 요구사항은?

아래는 마이크로 프론트엔드 아키텍처의 요구사항으로서 4개의 레이어로 나누어 진 이미지이다.

첫 번째 쉘 레이어는 앱의 진입점으로 일반적으로 경로(route)를 기반으로한 앱을 로드하는 컨테이너와 라우터 역할을 합니다.
두 번째 컴포지션 레이어는 도메인별 경로(route)와 페이지가 포함되어 있습니다.
세 번째 피쳐 레이어는 도메인별 비즈니스 로직, 스토리지 로직, 위젯을 포함하게 됩니다. 이러한 위젯은 코어 라이브러리와 추가적인 컴포넌트를 기반으로 개발됩니다.
마지막으로 코어 레이어는 도메인에 구애받지 않는 라이브러리가 포함되어 있고 이러한 라이브러리는 피쳐 레이어의 빌딩 블록을 제공합니다.

그린랩스의 자재플랫폼개발실에서는 쉘과 컴포지션 레이어를 하나로 합쳐 사용하고 있다.

유지해야할 레이어 비용을 줄일 수 있고 각 앱별 진입점을 스스로 결정하여 독립된 서비스로 분리할 수 있도록 하였습니다. 독립된 서비스는 서브 도메인 앱 레이어로 만들고 그린랩스에서 조직을 구성하는 스쿼드 조직에 맞게끔 저장소 구조를 유지하고 관리하고 있다.

이렇게 나뉘어진 마이크로 프론트엔드 아키텍처의 장점은?

  • 공유가능한 위젯 서비스와 페이지를 가지고 원활한 업그레이드가 가능하다.
  • 그리고 변경에 의한 사이드이펙트를 방지하고 리팩터링이 간단해 진다.
    👉 모노레포 저장소 모델의 장점이 생각나게 된다!

하지만 물론 극복해야할 단점도 존재한다.

  • 유지해야할 또 다른 레이어가 발생되고
  • 교육 및 학습 곡선이 있다.
  • 변경사항에 대한 통합 테스트도 필요하다.

그렇다면 이 복잡한 아키텍처를 유지하는 방법, 공유되는 패키지와 종속성 관리방법에 대해 알아보자!

마이크로 아키텍처를 지향하면서 여러 프로젝트와 공유 패키지를 만들고 종속성 관리를 하게 된다.

조직이 목적조직으로 변하고 각 제품별 멀티레포로 되어 있다고 가정을 해보자. 제품간 의존성이 있으면 해결하는데 많은 시간이 걸리게 된다. 이러한 목적 조직에서 멀티레포로 마이크로 프론트엔드 아키텍처가 유지되기는 어렵다고 볼 수 있다.


목적조직

특정 목적을 수행 또는 달성하기 위해 관련 인력들이 한 팀에 소속되어 있는 조직


마이크로 프론트엔드 아키텍처를 지향하고 이를 통해 목적 조직으로 구성되어 있는 조직 구조까지 고려하여 모노레포로 전환하기로 결정. 이전의 멀티레포를 유지하지 않고 과감하게 모노레포로 전환하기로 선택.

공통 패키지를 만들 필요가 없다면 멀티레포가 현명한 선택이다. 잘못된 모노레포의 선택은 오히려 해결하기 위한 이슈를 많이 생산할 수 있다. 이는 개발의 병목현상을 일으키고 프로젝트이 실패를 가져다 줄 수 있다.

모노레포의 좋은점

  • 하나의 수정으로 양쪽에 commit과 PR을 반여할 수 있다.
  • 모든 프로젝트를 쉽게 검색 및 수정할 수 있다.
  • 유사한 기능을 추상화하고 공유 패키지화하여 중복 작업을 줄일 수 있다.
  • 모든 사람이 모든 것을 보고 더 많이 저장소에 참여하면서 함께 한다는 "느낌", 제품에 대한 "소유권"이 발생됩니다. 이는 자발적으로 기술에 대한 집착과 지식을 공유하게 된다.
  • 그리고 회사에서 표준화하려는 환경과 배포 관리가 쉽다.
    - 예를 들어 package.json과 같은 종속성 관리나
    • 공유된 패키지의 재사용(예를들어 /design-system 또는 /common-util, /schema)을 쉽게 할 수 있고
    • 환경설정(ESlint, TSConfig 등)을 구성하거나
    • 테스트(unit, e2e)환경을 공통으로 구성하기 좋다.

모든 프로젝트들은 표준화되어 스타일시트의 일관성을 유지할 수 있다.

일관된 코어 라이브러리의 내용은 테스트 환경과 배포 설정을 위해 복사 붙여넣기로 간단하게 새로운 서브 도메인 레이어에 적용할 수 있다.

모노레포의 불편한 점

  • 저장소가 커지면서 체크인/체크아웃 성능이 저하되고
  • 누구나 코드를 변경할 수 없도록 PR을 구현해야 한다.
  • 그리고 복잡한 디렉토리 구조와
  • 복잡한 파이프라인은 추가적인 교육과 학습곡선을 만든다.
  • 모노레포는 하나의 저장소에 모든게 담겨 있다. 앱마다 다른 배포주기와 의존된 공유 패키지들은 복잡한 브랜치로 유지 관리를 힘들게 한다.

모노레포의 불편한점 보완

1. 낮은 체크인/체크아웃 성능
소스로부터 생성되는 아티팩트나 바이너리를 되도록 저장소에 포함시키지 않도록 했고 빌드된 바이너리라 아티팩트는 지속적인 빌드 통합과 배포(CI/CD)에서만 존재한다.

2. PR 구현
PR 구현은 Github처럼 대부분의 버전 제어 시스템에서 제공.
Github의 PR과 테스트는 코드 변경을 막고 지식공유를 위한 장점을 제공한다.

자유롭게 코드리뷰와 공통된 이슈를 해결하기 위해 노력하게 되면서 자연스럽게 조직의 소속감을 느끼게 된다.

3. 복잡한 디렉토리 구조와 파이프라인
모노레포 도구를 이용해 불편한점을 보완한다.

  • 모노레포 도구들

    그린랩스에서는 Turborepo 모노레포 도구를 사용하고 있다. Vercel 배포 플랫폼을 이용하는데 있어 시너지 효과가 있어 선택했다.

  • 모노레포 도구의 기능
    1. 의존성에 따른 병렬 실행
    저장소의 모든 앱과 패키지를 빌드하는데 패키지에 대한 의존성을 알아서 정렬시키고 여러 패키지에 대해서 순서대로 빌드하지 않고 의존성에 따라 병렬로 실행해 빌드해 준다.

    1. 수정시 영향 받는 프로젝트만 빌드 & 테스트

      영향 받는 프로젝트만 빌드하게 된다면 빌드할 때 소요되는 시간이 줄어든다.

    2. 머신에서 같은 것을 두 번 빌드하거나 테스트하지 않음

      만약 수정없이 빌드를 다시 수행하게 된다면 빌드를 다시 하는데 매우 짧은 시간이 소요된다.

  • 복잡한 브랜치 병합(Merge)와 통합(Integration)
    모노레포는 단일 저장소이므로 다른 팀, 다른 프로젝트, 다른 패키지, 라이브러리들이 다 함께 존재하고 있다.

    모노레포에서 협업하는 모든 팀들이 릴리즈했다고 가정해보자. 릴리즈 후에는 바로 다음 프로젝트를 진행하게 되며, 제품 백로그는 계속 재생산하게 된다.

    A팀은 릴리즈 준비가 되어 배포하고 싶지만 B팀에 이슈가 있어 해당 이슈를 우선적으로 처리한 후 개발할 예정이다. A팀은 B팀에 종속성을 가지고 있어 배포할 수 없다. 빨리 배포하고 싶지만 종속성을 가지고 있기 때문에 그럴 수 없다. B팀은 이슈를 해결하고 있어 뒤늦게 개발을 시작한다. 그리고 v2.0이 배포가 된다.

    그동안 A팀은 v3.0개발을 준비하고 개발을 하고 있었다. 여기서 A팀은 v2.0에서 어떤 버그가 있을지 모르는 상태에서 v3.0개발 준비를 해야한다. 그 와중에 의존성이 없는 C팀은 v2.0을 배포하였다.

    이처럼 팀간 의존성이 있는 배포와 그렇지 않은 배포가 있고 버전은 각 팀별로 다르게 관리가 된다.

    또한 팀에서 애플리케이션은 여러개의 작은 부분으로 나뉘고 각 부분을 별도의 배포와 테스트, 버전 업그레이드 일정이 있다. 각 팀마다 버전은 다르고 여러 종속성과 이를 위한 배포 관리를 누군가 조율하지 않으면 서비스에 장애가 발생할 수도 있다.

    멀티레포에서는 팀간 저장소가 달라 브랜치 머지시 충돌될 일이 없다. 배포순서나 일정을 조율하면 해결할 수 있다. 하지만 모노레포를 사용시 코어 라이브러리를 수정하거나 공유 패키지가 수정될 수도 있다. 이러한 수정은 잘못하면 모든 시스템에 영향을 미칠 수 있다.

    👉 추상화된 브랜치 패턴과 기능 플래그를 이용하여 해결

    • 추상화된 브랜치(Branch by Abstraction)
      애플리케이션의 큰 변화를 만들 때 주 흐름을 점진적으로 반영하는 패턴으로 추상화된 브랜치는 대체될 컴포넌트와 의존하고 있는 코드들 사이에 추상화 레이어를 추가하고, 새로 도입할 컴포넌트는 의존하지 않도록 커밋한다.
      순차적으로 코드들이 새로운 컴포넌드에 의존하도록 변경하고, 이러한 작업이 완료되면 기존 컴포넌트는 제거하는 패턴이다.

      		자동차 바퀴를 바꾸는 그림으로 예시를 들면 핑크색의 추상화 레이어를 만들고 바퀴를 하나씩 만든다. 나중에 한꺼번에 모든 바퀴를 토글형식으로 갈아치운다. 이러한 패턴은 올드 컴포넌트와 뉴 컴포넌트가 동시에 존재할 수 있고 개발하면서 발생되는 의존성 이슈를 미리 발견하고 빠른 배포가 가능하다.

      기존 default 컴포넌트를 사용할지 아니면 다른 버전 컴포넌트를 사용하지 코드 내에서 동적으로 결정하고, 추상화 레이어를 만들어 점진적으로 반영할 수 있다.

      전체 애플리케이션 내에서 특정 팀이나 버전에 종속되지 않고 미리 개발해 배포가 가능하다.

    • 기능 플래그(Feature Flags)

      기능 플래그를 사용할 경우 응용 프로그램이나 서비스의 특정 기능을 켜고 끄는 방법으로 특정 UI 요소를 표시/숨김을 전환하거나, 특정 로직을 On/Off 하는 등의 기능 릴리즈 관리가 가능하다.

      	- 개발 환경에서는 기능을 활성화하고 스테이징이나 프로덕션에서는 기능을 비활성화 처리가 가능하다.
      	- 서비스마다 플래그를 도입하여 끄고 배포한 뒤, 켜짐 상태로 전황하여 배포없이 동적으로 모니터링이 가능하다.
      	- 기능에 문제가 생길경우 바로 해당 기능을 비활성화 처리할 수 있다

.

이슈를 해결하는 방법 정리

  1. 저장소가 커지면서 낮은 체크인/체크아웃 성능
    => 아티팩트나 바이너리(CI/CD에서 출력)는 되도록 저장소에 포함시키지 않는다.
  2. PR(Pull Request) 구현. 그렇지 않으면 누구나 코드 변경 가능
    => Github Branch rules와 PR & Test 통해 해결
  3. 복잡한 디렉토리 구조 & 파이프라인
    => 모노레포 도구를 이용
  4. 복잡한 브랜치 병합(Merge)와 통합(Integration)
    => 추상화된 브랜치 패턴과 기능 플래그 활용

기존 멀티레포를 모노레포로 만들고 싶을 때

  1. 앱 폴더에 모든 프로젝트를 넣는다.
  2. 그 후에 공통화/패키지화 시킬 수 있는 것들을 리팩토링 진행
  3. 그 후엔 자연스럽게 코드리뷰를 통해 저장소가 점점 효율화되어진다.

목적 조직의 사일로를 구성하고 있는 조직의 경우 고립된 당사자를 구출하고 프로젝트가 원활하게 이뤄질 수 있도록 해야 한다.
모노레포로 이동 시 사일로로서 고립되어 있지만 하나의 소속감으로 소유권을 가지고 피드백과 토론이 이루어질 수 있다.
새로운 서비스나 앱에 별도의 개발 스택을 사용하면서 기술적으로 실험적인 개발과 경험을 개발자들에게 전달할 수 있다.
모노레포에 참여한 개발자들은 공통된 관심사의 경우 재사용성과 확작성을 유지하려고 노력하게 된다.


아티팩트

  • 일반적인 의미
    - 일부 문맥에서 "아티팩트"라는 용어는 해당 시스템이나 프로세스에 고유하지 않고 오히려 오류나 기타 의도하지 않은 영향의 결과인 시스템 또는 프로세스의 기능이나 측명을 나타내는 데에도 사용된다.
    - 소프트웨어 개발에서 아티팩트는 컴파일된 바이너리, 문서 또는 테스트 결과와 같이 소프트웨어 개발 프로세스의 일부로 생성된 특정 출력 또는 결과물이다. DevOps 및 CI/CD(지속적인 통합/지속적인 전달) 워크플로에서 아티팩트는 일반적으로 나중에 사용하기 위해 저장되는 빌드 프로세스의 최종 제품이다.

참고자료

https://inpa.tistory.com/entry/JEST-%F0%9F%93%9A-jest-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC

https://simsimjae.medium.com/%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%AC-252d7f11c6fa

https://contents.premium.naver.com/3mit/wony/contents/220505105924891hK

PoV, MVP

마이크로서비스 아키텍처

목적조직과 기능조직

profile
hello, world!

0개의 댓글