Cloud Native 와 microservice 가 주목 받음에 따라 Frontend 에서도 유사한 구조의 architecture 가 급부상하고 있다. Micro Frontend (MFE) 가 그 주인공으로, 최근 이 구조를 도입하려는 시도를 심심치 않게 목격할 수 있다. MFE 는 무엇이고, 어떤 상황에 적합할까?
이 글은 Cloud Native Application and MSA 에 대한 배경지식을 바탕으로 한다.
A micro frontend is an architectural pattern for web development, where independently developed frontends are composed into a greater whole. - wikipedia
MFE 는 독립적 으로 개발이 진행된 application 들을 하나의 domain 으로 접근 가능하도록 제공하는 frontend architecture 이다. 이 구조는 각 서비스를 담당하는 frontend 팀이 제각각 자율적인 autonomous 개발 환경을 구축하여 복잡한 dependency 공유 체계를 거치지 않고 개발을 진행하는 것을 가능하게 해준다. 이 덕분에 다른 framework 기반의 서비스를 연결할 수 있다는 부가적인 효과도 생긴다.
MFE 도입의 이점을 정리하면 다음과 같다.
하지만, 이는 MFE 의 근본적인 도입 목적에 따른 부가 효과에 불과하다.
언급한 장점들을 생각하면 MFE 를 도입하지 않을 이유가 없어 보인다. 현재 application 생태계에서 주로 문제가 되는 dependency 문제, 버그 픽스나 사소한 서비스 업데이트를 위한 application 전체의 재배포 과정 등을 해결해줄 수 있는 완벽한 기술적 대안으로 보이기 때문이다. 하지만, MFE 의 도입은 위의 장점들을 넘어 기존에 확보했던 기술적 이점들까지도 역행시킨다.
MFE 의 도입은 SPA 에서 MPA 로의 역행을 의미한다. SPA 가 가져다주는 shared application context, optimized bundle loading 등을 모두 져버리는 선택이기 때문이다. 이는 기술적인 측면에서 진보는 커녕, 오히려 후퇴하는 셈이다. cross-app communication 및 coordination 을 포함하여 복잡성이 증가할 뿐더러, 성능을 유지 시키기 위해 분리된 모든 application 에 Bundle Optimization 에서 다룬 최적화 기법들을 전부 적용해야 한다.
MFE 가 어떻게 적용되길래 이런 엄청난 복잡성 증가를 불러올까?
MFE 를 적용하는 방법에는 크게 두 가지가 있다.
두 방법 모두 단순 설명만으로는 단순하기 그지 없다. route 를 덮어써서 서비스 접근에 연속성을 부여하거나, npm 패키지처럼 module 형태로 export 하고 import 만 하면 되지 않는가 싶다. 하지만 문제는 보이지 않는 곳에 숨어있다.
이 route 를 덮어쓰는 방식은 단순해보이지만 사실 MFE 구조를 제일 정확하게 반영한 방법이다. Next.js 는 next.config.js
의 rewrites
설정을 통해 이 기능을 제공한다.
Multi-Zones Diagram, Next.js
Multi-zone 방식은 각 application 이 독립적인 서버에서 실행되며, shell application 이 특정 route 패턴에 따라 요청을 해당 application 으로 proxy 하는 구조다. 예를 들어 /shop/*
경로는 쇼핑 서비스로,/admin/*
경로는 관리자 서비스로 라우팅된다.
이 방식의 핵심 문제는 page transition 마다 전체 application 을 새로 로드해야한다는 점이다. 각 서비스가 독립된 서버에서 실행되기 때문에 route 이동 시 브라우저는 완전히 새로운 페이지를 로드하게 되고, 이는 SPA 의 핵심 장점인 빠른 네비게이션을 포기하는 것을 의미한다. 또한 이 방법은 Component 단위로의 서비스 분리가 불가능하다. 하지만 각 application 이 분리된 dependency 및 life cycle 을 가짐으로써 완전한 독립성을 확보할 수 있다.
Module Federation 은 Multi-zone 의 한계를 극복하기 위해 등장한 방법이다. 각 application 을 runtime 에서 동적으로 로드할 수 있는 module 형태로 빌드하여, 하나의 shell application 내에서 여러 micro frontend 를 조합하는 방식이다.
Multi-zone 과의 핵심 차이점은 Single Page Application 특성을 유지한다는 것이다. 각 micro frontend 가 JavaScript module 로 동작하기 때문에 page transition 시 전체 페이지를 새로 로드할 필요가 없고, component 단위의 세밀한 분리도 가능하다.
하지만 이 방식도 심각한 문제점들을 안고 있다:
또한, Multi-zones 와 다르게 dependency 종속성이 생기며, 일부 모듈의 업데이트를 동기화하여 진행하게 되어 완전한 독립성을 갖추지 못한다는 점도 문제가 될 수 있다.
두 방법 모두 JavaScript Bundle 크기의 증가를 초래하며, 상당 기간을 세밀한 Optimization 에 할애하지 않으면 서비스의 성능이 심각하게 저하되는 결과를 맞게 된다.
두 가지 방식의 특징들을 바탕으로, 다음과 같은 형태에 MFE 를 적용하는 것이 가장 적합하다고 판단할 수 있다:
위의 두 방법 모두에서 등장하는 Shell Application 의 정체는 무엇일까? Shell Application 은 MFE 구조에서 orchestrator 역할을 담당하는 중앙 집중식 application 이다. 사용자가 실제로 접근하는 진입점이며, 각 micro frontend 들을 통합하고 관리하는 책임을 진다.
Shell Application 의 주요 책임은 다음과 같다:
문제는 이 Shell Application 자체가 또 하나의 복잡한 시스템이 된다는 점이다. 단순한 routing 이나 module loading 을 넘어서, 실질적으로 분산된 시스템의 coordinator 역할을 수행해야 하므로 그 자체로 상당한 복잡성을 갖게 된다. 결국 MFE 를 도입한다는 것은 기존의 monolithic frontend 에 추가로 이런 orchestration layer 를 구축하고 유지보수해야 한다는 의미이기도 하다.
그런데 Shell Application 이 맡게 되는 책임을 살펴보면 이상한 점을 확인할 수 있다. Frontend 에 속한 그 분류와는 전혀 맞지 않는 backend 성격의 책임을 갖는 것이다. 많은 MFE 소개 글들이 Shell Application 을 Frontend 서비스로 언급하지만, 과연 이를 Frontend 영역으로 분류하는 것이 맞을까?
"It was very slow to iterate upon and experiment, especially when it came to making changes across multiple views like updating the visual style."
Spotify 는 MFE 에서 벗어난 사례다. 초기에 iframe 기반의 micro frontend 구조를 사용했지만, 2016년에 완전히 다시 작성하기로 결정했다. 그 이유는:
결국 React + Redux 기반의 단일 application 으로 재구축한 결과, 모든 핵심 지표에서 이전 web player 를 앞서는 성능을 달성했다. Spotify 사례는 MFE 의 이론적 장점들이 실제 환경에서는 오히려 발목을 잡을 수 있음을 보여주는 대표적인 예시다.
흥미롭게도 Spotify 의 MFE 실패는 동시기의 조직적 문제와 밀접한 관련이 있다. "the Spotify Model" 에 대한 회고를 여기서 확인해볼 수 있는데, 자율적인 squad 들이 서로 다른 기술 스택을 사용하며 coordination 없이 개발하던 조직 구조가 바로 MFE 의 maintenance complexity 와 development friction 을 야기한 근본 원인이었다. Conway's Law 에 따르면 소프트웨어 구조는 조직 구조를 반영하는데, Spotify 는 조직적 문제를 기술적 architecture 로 해결하려다 실패한 셈이다.
NIA 한국지능정보사회진흥원 이슈리포트 에서 발행한 글에서 Micro Frontend 의 Best Practice 로 소개된 Facebook 의 BigPipe 서비스 개선 사례는 전형적인 분류 오류의 예시다.
BigPipe 는 2009년 Facebook 이 웹 페이지 로딩 시간을 줄이기 위해 개발한 서버 사이드 렌더링 최적화 기법이다. 웹 페이지를 "pagelets" 라는 작은 단위로 나누어 서버에서 병렬로 처리한 후 클라이언트로 스트리밍 하는 방식이다. 이는 본질적으로 백엔드 아키텍처 최적화에 해당하며, 현재 우리가 논의하는 MFE 개념과는 완전히 다른 영역이다.
BigPipe 의 핵심은:
이런 기법을 MFE 로 분류하는 것은 앞서 언급한 Shell Application 을 Frontend 로 분류하는 것과 같은 맥락의 개념적 혼동이다. MFE 영역의 미성숙함을 보여주는 또 다른 사례로, 성능 최적화 기법과 아키텍처 패턴을 혼동하고 있는 것이다.
이 글에서 살펴본 바와 같이, MFE 는 아직 개념적으로 정립되지 않은 미성숙한 영역이다. Shell Application 을 Frontend 로 분류하거나, BigPipe 같은 백엔드 최적화 기법을 MFE Best Practice 로 소개하는 등의 사례들은 이 분야가 얼마나 혼재된 상태인지를 보여준다.
더 중요한 것은 MFE 의 본질이 기술적 해결책이 아닌 조직적 문제에 있다는 점이다. MFE 도입을 고려한다면 먼저 조직적 차원에서 접근해야 한다:
성능 향상이나 개발 효율성 같은 기술적 목적으로 MFE 를 도입하려 한다면, 그보다는 Bundle Optimization 이나 모듈화 같은 검증된 기법들을 먼저 고려해보는 것이 현명하다. MFE 는 조직적 문제에 대한 마지막 수단이어야 하며, 그 복잡성과 비용을 충분히 이해한 후에 신중하게 결정해야 할 아키텍처 선택이다.
Introduction to the Micro Frontend, with common misconceptions.