
Micro-Frontend Architecture 위한 수단
Module Federation은 정말 처음 들어보는 생소한 용어였다. 해당 기술에 대해서 알아보면서 이게 Micro-Frontend Architecture를 위해서 사용하는 일종의 수단이구나 생각했다.
Module Federation
Module Federation
정의
서로 다른 프로젝트에서 하나 이상의 빌드된 코드를 런타임에 동적으로 로드하고 공유할 수 있게 하는 기술
왜 사용하는가?
개념을 알아보면서 느낀 점은 이 기술은 마이크로 프론트엔드와 매우 밀접한 연관을 갖고 있다는 것이었다. Module Federation을 사용하는 가장 큰 이유는 대규모의 프로젝트를 소규모의 독립적인 프로젝트로 분리하는데에 있다고 느꼈다.
그렇기 때문에 일반적으로 혼자서 할 수 있는 혹은 2~4인의 소규모 팀에서는 경험해보기 힘든 분야라고 생각이 들었다. 따라서 큰 규모의 프로젝트를 한다고 상상했을 때 이 기술을 왜 쓰는지 이해할 수 있었다.
1. 독립적인 스쿼드 운영
혼자서 개발할 때나 팀프로젝트를 한다면 각자 하나씩 기능을 맡아서 구현해도 전혀 문제될게 없고, 기능 간 서로 연관이 깊기 때문에 머지 충돌이 생겨도 금방 해결할 수 있다.
하지만 대규모 프로젝트거나 기업에서의 애플리케이션은 매우 크기 때문에 머지 충돌이 생길 경우 해결에 시간이 더 오래걸리고, 이해 관계가 얽혀있는 경우가 많다.
이럴 때 하나의 레포지토리에서 100명이 넘는 개발자가 개발을 하는 경우에는 효율이 나오지 않을 수 있다. 빌드와 배포에서도 하나의 팀이 중요한 버그 수정사항을 커밋하면 전체 앱을 재빌드 해야하기 때문이다.
하지만 Module Federation과 Micro Frontend Architecture를 사용하면 하나의 앱의 여러 부분을 독립적으로 운영할 수 있게 된다. 나의 블로그로 예시를 든다면, A 팀은 메인페이지를 담당하고, B 팀은 블로그 글 리스트를 담당하는 식이다.
2. 점진적 마이그레이션
가장 큰 이유는 위의 독립적인 스쿼드 운영과 마이크로프론트엔드 아키텍쳐이지만, 이 점진적 마이그레이션은 부가적으로 따라오는 장점이라고 생각했다.
마이크로 프론트엔드로 구성하기 시작하면 대규모 업데이트나 대규모 마이그레이션 작업을 점진적으로 하기 좋다.
const App = () => {
return (
<>
<LegacyHeader /> {/* jQuery */}
<NewReactDashboard /> {/* React - 새로 작성 */}
<LegacyFooter /> {/* jQuery */}
</>
);
};
각자가 독립적인 앱이기 때문에 마이그레이션 과정 중 생기는 버그도 전체 앱에 영향을 미치지 않고 독립적으로 해결 할 수 있다.
핵심 개념
Host
호스트는 메인 앱을 지칭, 주로 Remote 패키지들을 로드하는 주체가 되는 애플리케이션
Remote
독립적인 마이크로 앱, 다른 앱에서 사용할 수 있도록 엔트리포인트를 만들어 노출시킬 필요가 있음.
사용 예시
// Host 앱에서 Remote 컴포넌트 사용
import React, { lazy, Suspense } from 'react';
// Remote 앱의 컴포넌트를 동적으로 import
const ProductList = lazy(() => import('productApp/ProductList'));
const UserProfile = lazy(() => import('userApp/UserProfile'));
function App() {
return (
<div>
<h1>메인 애플리케이션</h1>
<Suspense fallback={<div>제품 목록 로딩중...</div>}>
<ProductList />
</Suspense>
<Suspense fallback={<div>사용자 프로필 로딩중...</div>}>
<UserProfile />
</Suspense>
</div>
);
}
장단점
장점
- 마이크로 프론트엔드 아키텍쳐를 구현하기 위해 사용하는 방법 중 하나로 사용될 수 있음
- 점진적 마이그레이션이 가능하다.
- 기존 레거시를 현대화된 프로젝트로 개선하기 위해서는 여러가지 방법이 있다. 하나는 현대화된 프로젝트를 만들고 한번에 레거시를 대체하는 방법, 다른 하나는 Module Federation을 사용해서 점진적으로 일부 앱을 하나하나 현대화하는 방식
- 하나의 공유 컴포넌트 라이브러리로 사용가능
- 여러가지 마이크로 앱에서 사용되는 컴포넌트의 경우 중복을 최소화하기 위해서 common 앱으로 분리해서 동적으로 로드하여 사용.
단점
단점들은 어쩔 수 없이 따라오는 부분들이 많은 것 같다. 특히, 대규모 서비스를 운영하게 되면 필연적으로 생길 수 밖에 없는 문제들이 단점이 될 것 같다.
내가 생각한 단점
- 어렵다, 러닝커브가 높다고 생각됨
- 마이크로 프론트엔드의 어려움과 결이 같다고 생각함
- host에서 어떻게 remote app들을 사용할지 정의하는 것이 중요할 것 같음
- 각자 remote app을 개발하는 것은 어렵지 않을 것 같지만, 그걸 하나로 합치고, 공통에서 사용해야하는 api를 만드는 것과 각 앱 간의 통신에서 어려움을 느낄 것 같다.
실제 단점
개발 환경의 복잡성
각 리모트앱과 호스트앱을 모두 실행 필요
Host-Remote 통합 설계의 중요성
Host에서 Remote 앱들을 어떻게 사용할지 정의하는 것이 매우 중요하다. 잘못 설계하면 독립성의 장점을 잃고 강한 결합이 생길 수 있다.
앱 간 통신과 공통 API 설계
각 Remote 앱을 개발하는 것은 어렵지 않을 것 같지만, 이를 하나로 합치고, 공통에서 사용해야 하는 API를 만드는 것과 각 앱 간의 통신에서 어려움을 느낄 것 같다.
궁금한 점
앱 간의 통신은 어떻게 하는가?
앱 간 통신이 필요한 상황이 있다고 가정해보자. 예를 들어 장바구니 리모트앱, 결제 리모트앱이 나누어져있다고 한다면, 장바구니에서 결제를 클릭했을 때 장바구니에 담긴 상품 데이터를 어떻게 결제 페이지로 보낼 수 있을까?
여러 방법들 있지만, 유용하게 사용할 수 있을 것 같은 방법으로는 네 가지를 뽑아 볼 수 있을 것 같다.
- 전역상태 라이브러리 or ContextAPI
- URL 쿼리파라미터로 데이터 전달
- 백엔드 동기화
- 이벤트 버스
모든 remote 앱들을 호출하는 주체인 host 앱에서 전역 상태 Provider를 제공하면 그 아래에서 생성되는 리모트 앱들에서도 전역 상태를 공유할 수 있다. 이걸 통해 통신하는 방법이 있다.
이벤트 버스는 중앙에서 메시지를 전달해주는 시스템으로, A 앱에서 특정 이벤트를 emit하면 그 이벤트를 구독하는 B 앱에서 이벤트에 대한 처리를 진행하는 방식이다. Event Emitter를 생각하면 쉬울 것 같다. 이런 방식으로 remote 앱 간에 메시징이 가능하다.
이벤트 버스
웹뷰와의 차이점
런타임에 모듈로 통합되는 것과 웹 뷰의 차이점은 무엇일까?
먼저 웹 뷰는 완전히 독립된 컨텍스트와 메모리를 갖는다는 점이 차이점이다.
하지만 Module Federation은 런타임에 통합된다는 것이 결정적인 차이점이다. 개발은 독립적으로 하지만 실제 실행은 하나의 앱과 같이(메모리 공유 등) 동작한다고 볼 수 있다.
결론
Module Federation은 마이크로 프론트엔드를 구성하기 위해서도 사용하고, 가독성과 개발 효율성을 위해서 사용하기도 한다. 핵심은 특정 모듈을 런타임에 불러와서 사용하기 위함이다. 마이크로 프론트엔드에서 각 마이크로앱을 구현하고 Module Federation을 사용해서 host 앱에서 합치는 방식을 사용한다.
Module Federation이라는 용어에 꽂혀서 글을 작성해봤는데, 작성하면 할 수록 내용이 너무 방대해져서 조금 두서없이 작성한 것 같다. Module Federation은 개념느낌이고, 이걸 설명하려면 Micro Frontend 구조를 설명해야하기 때문에..
