Microfrontend Architecture

최경식
11 min readJul 17, 2022

좋은 아키텍처란

좋은 아키텍처는 제품의 품질을 유지하고 제품의 수명 내내 쉽고 간편하게 관리할 수 있도록 도와주는 효과적인 소프트웨어의 뿌리입니다.

결론적으로 수정하기가 더 쉽고 편리하기 때문에 장기적으로 수익성이 있습니다. 이를 통해 고객의 변화하는 요구 사항을 기민하게 충족시키면서 개발자의 시간과 노력을 절약할 수 있습니다.

효율적인 프로그래밍 아키텍처에는 아래와 같은 품질 속성 세트가 있습니다.

  • Functionality(기능): 소프트웨어가 필요한 목적에 대해 수행하는 정도를 나타냅니다.
  • Usability(사용성): 소프트웨어를 쉽고 편리하게 사용할 수 있는 수준을 나타냅니다.
  • Reliability(신뢰성): 주어진 상황에서 의도한 기능을 제공하는 제품의 능력을 나타냅니다.
  • Supportability(지원 가능성): 개발자가 최소한의 변경 또는 변경 없이 한 플랫폼에서 다른 플랫폼으로 소프트웨어를 전송할 수 있는 기능을 말합니다.
  • Performance(성능): 자원 활용도, 처리 속도, 응답 시간, 생산성 및 처리량을 고려하는 것을 말합니다.
  • Self-Reliance(자립심): 다운타임을 겪더라도 최적의 성능을 위해 독립적인 활동을 할 수 있는 능력을 말합니다.

마이크로…

마이크로 서비스

마이크로 서비스는 애플리케이션이 명확한 경계와 책임이 있는 독립 구성 요소 집합으로 분할되는 접근 방식입니다.

각각은 개별적으로 작동하고 하나의 고유한 기능을 수행합니다. 이러한 시스템은 뛰어난 모듈성과 확장성 기능을 갖추고 있어 유지 관리, 테스트 및 개발이 더 쉽습니다.

또한 전체 시스템을 수정하지 않고도 관심 있는 마이크로 구성 요소를 업데이트, 추가 또는 삭제할 수 있습니다.

일반적으로 소규모 프로젝트 또는 개념 증명(POC, proof-of-concept)은 구현이 간단하고 빠르기 때문에 모놀리식(monolithic) 애플리케이션으로 시작됩니다.

모놀리식 애플리케이션은 분리할 수 없는 하나의 단위입니다. 모든 것은 동일한 코딩 언어와 데이터베이스를 사용하여 동일한 플랫폼에서 작성됩니다.

모놀리스의 일부에 업데이트가 필요하거나 새로운 기능이 추가될 때 어려움이 따릅니다.

종속성을 하나씩 풀다보면 결국 전체 시스템을 덮어써서 기능을 변경하거나 추가하게 됩니다. 그리고 또 다른 기능 요청이 옵니다…

모놀리스는 구성 요소의 긴밀한 결합(tight coupling)으로 인해 아키텍처가 단단해집니다. 모든 다음 업데이트는 이전 업데이트보다 더 고통스럽습니다.

마이크로 서비스 기반 애플리케이션은 가장 많이 채택된 소프트웨어 아키텍처 유형입니다.

모듈식 소프트웨어 개발, 유연한 확장 및 크고 복잡한 애플리케이션의 빠른 제공을 가능하게 합니다.

처음부터 응용 프로그램의 적절한 디자인을 사용하는 것이 중요합니다. 팀의 성능 최적화를 위해 잘 채택된 접근 방식입니다.

마이크로 프론트엔드

지난 10년 동안 프론트엔드 애플리케이션의 복잡성이 증가했습니다.

많은 비즈니스 로직이 더 이상 백엔드에 보존되지 않고 클라이언트 측에서도 발견됩니다. 이는 애플리케이션 복잡성에 영향을 미치고 새로운 모놀리스인 프론트엔드 애플리케이션을 도입합니다.

프론트엔드가 커짐에 따라 여러 책임 영역(예: navigation, user, authorization)을 유지하면서 프론트엔드 애플리케이션은 단일 애플리케이션으로 개발, 배포 및 호스팅됩니다.

그리고 일반적으로 모듈, 라이브러리, 때로는 애플리케이션별로 디렉토리를 사용하는 동안 코드베이스에서 분리되어 있는 것을 발견하게 됩니다.

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

이를 통해 조직은 복잡한 UI 애플리케이션을 더 작고 관리하기 쉬운 조각으로 나눌 수 있을 뿐만 아니라 팀이 전체 앱에서 일관된 사용자 경험을 유지하면서 잘 정의된 제품 또는 비즈니스 도메인에서 종단 간 작업을 할 수 있습니다.

이 접근 방식의 주요 이점 중 하나는 팀이 자율적으로 배치 가능한 UI 애플리케이션에서 독립적으로 작업할 수 있게 하여 결합의 위험을 줄이고 구분된 컨텍스트 주위에 명확한 경계를 정의하여 종속성을 최소화할 수 있다는 것입니다.

그럼으로써 더 빠른 개발, 디버깅 및 테스트 흐름을 만들며 더 작은 청크(chunks)를 생성하여 성능을 향상시킬 수 있습니다. 결론적으론 유연성(flexibility)과 민첩성(agility)을 제공할 완전히 격리(isolated)된 다중 애플리케이션 플랫폼의 형태라면 마이크로 프론트엔드 아키텍처일 것입니다.

마이크로 프론트엔드의 요구사항

Monolith

대부분의 모놀리스 프론트엔드 애플리케이션은 다음의 레이어로 구현되었을 것 입니다.

  • Composition layer — routes가 있는 일련의 애플리케이션 페이지
  • Widgets layer — composition layer에서 찾은 다양한 페이지를 만드는 데 사용되는 도메인 관련 component들을 보유
  • Business logic layer — 도메인 비즈니스 로직을 담당하는 서비스 및 유틸리티 세트를 보유
  • Communication layer — 다른 서비스 제공자(예: 백엔드 서비스)와 통신하는 데 사용되는 서비스 세트를 보유
  • Storage layer — 스토리지 개체에 데이터를 유지하는 논리를 보유(메모리: State, hooks, 디스크: local-storage, indexedDB, cookies)

우리는 이러한 모든 응용 프로그램이 사용자 브라우저에서 단일 모놀리스로 호스팅된다는 점을 염두에 두어야 합니다.

Micro

마이크로 프론트엔드 애플리케이션에 대한 요구 사항을 검토해 보겠습니다.

  1. 각 애플리케이션은 독립 실행형 단위로 빌드, 테스트 및 제공되어야 합니다.
  2. 단일 애플리케이션의 수정된 결과는 다른 애플리케이션에서 사용할 수 있어야 합니다.
  3. 애플리케이션 위젯과 서비스는 재사용 가능하고 상호 교환 가능해야 합니다.
  4. 애플리케이션 내부 모델 및 비즈니스 로직의 캡슐화 — 수정이 애플리케이션 소비자에게 영향을 미치지 않아야 합니다.
  5. 수정에 따른 종속성 그래프의 식별 — 관련 테스트 모음 및 빌드만 트리거하는 데 도움이 됩니다.

이에 따라 다음 접근 방식을 검토해 보겠습니다.

모듈 연합(module federation) 방식을 사용하여 공유 라이브러리를 설정할 수 있습니다.

모듈 연합(module federation)은 여러 개별 빌드가 단일 애플리케이션을 형성해야 합니다. 이러한 별도의 빌드는 서로 종속성이 없어야 하므로 개별적으로 개발 및 배포할 수 있습니다.

이 구성의 일부로 npm 패키지 버전 관리 규칙을 사용하여 만족스러운 패키지 버전을 설정할 수 있습니다.

이 접근 방식은 모노리스를 4개의 레이어로 나누는 데 도움이 됩니다.

  • Core Library — 도메인에 구애받지 않는 라이브러리가 포함되어 있으며 이러한 라이브러리는 기능 라이브러리 레이어의 빌딩 블록을 제공합니다.
  • Feature application layer — 도메인별 비즈니스 로직, 스토리지 로직 및 위젯이 포함됩니다. 이러한 위젯은 핵심 라이브러리 구성 요소 키트 및 특정 책임 영역의 일부인 추가 구성 요소를 기반으로 개발되었습니다.
  • Composition applications — 도메인별 경로와 페이지가 포함되어 있습니다. 이러한 페이지는 “Feature application” 계층의 일부로 개발된 위젯, 서비스 및 비즈니스 로직을 기반으로 구축됩니다.
  • Shell — 애플리케이션의 진입점은 일반적으로 경로를 기반으로 각 마이크로 애플리케이션을 로드하는 컨테이너 및 라우터 역할을 합니다. 셸 응용 프로그램은 권한 부여 논리(authorization logic)를 트리거할 수도 있습니다.

구조:

- apps
- user
- feed
- navigation
- shell
- libs
- users-lib
- feed-lib
- navigation-lib
- design-system-lib
- auth

장점

  • 애플리케이션 간에 공유 가능한 위젯 서비스 및 페이지
  • 원활한 업그레이드
  • 주요 변경(breaking changes)에 의한 사이드이펙트를 방지
  • 리팩터링은 캡슐화 덕분에 더 간단

단점

  • 유지해야 할 또 다른 레이어가 발생
  • 교육 및 학습 곡선(learning curve)
  • 주요 변경사항(breaking changes)에 대한 통합 테스트가 필요합니다.

결론

마이크로 프론트엔드 아키텍처의 구조를 만들고 유지보수하기 위해선 해당 각 각의 Composition 레이어의 독립된 빌드환경을 유지하고 빌드는 효율적으로 이루어져야 합니다. 이를 위해서 Monorepo 저장소 모델을 이용하여 각 App Shell을 포함한 Composition 레이어를 유지합니다.

App Shell을 Composition 레이어에 포함시키는 것은 하나의 Composition 레이어를 독립된 서비스로 분리할 수 있는 subdomain 단위로 보고 각 목적 조직의 구성에 맞는 저장소 구조를 유지하기 위해서 입니다(기능조직 vs 목적조직).

그리고 App Shell을 Composition 레이어에 합침으로써 관리해야할 레이어를 하나 줄여 유지해야할 레이어 비용을 줄이는데 있습니다.

환경에 대한 템플릿을 모노레포에서 제공하도록 하여 신규 프로젝트 런칭을 Copy & Paste하여 바로 개발 시작을 하거나 같은 저장소에 관련 프로젝트 코드가 전부 들어 있기 때문에 개선이나 모르는 프로젝트들에 대해서도 기웃거리게 됩니다. 그러면서 일종의 오너십이 생기도록 돕습니다. 물론 새로운 개발 스택이나 도전적 과제들을 쉽게 시도하고 피드백 받을 수 있습니다.

Monorepo 모델을 가져가면 공유 서비스 또는 라이브러리를 하나의 저장소에서 관리할 수 있고 하고 각각의 서비스를 별도 배포하려는 방향성을 가지게 됩니다.

별도의 서비스로 배포한다는 의미는 별도의 서브도메인으로 배포한다는 얘기 입니다.

ex)

서브도메인으로 배포한다는 것은 별도의 스택과 배포프로세스를 진행한다는 의미이며 Monorepo를 통해서 공통 라이브러리 및 서비스를 정의하고 디자인 시스템을 공통으로 사용할 수 있다는 것을 의미합니다.

서비스들의 테스트를 하나로 통합하여 다양한 도구와 기술을 사용하여 작성된 애플리케이션을 일관되게 구축하고 테스트하고 전체 서비스에 문제가 있는지를 모니터링 할 수 있습니다.

새로운 서비스에 별도의 스택을 사용할 수 있으므로 기술적으로 실험적인 개발과 경험을 개발자에게 전달할 수 있을것으로 생각합니다.

목적 조직으로 나뉘어 있지만 하나의 소속감으로 소유권(OWNERSHIP)을 가지고 피드백과 토론이 이루어질 수 있습니다.

참고

관련 내용은 아래 영상에서도 소개되고 있습니다.

--

--

최경식

Lead Software Engineer, Client Side/Lightweight Full stack