“Simplicity is the ultimate sophistication” – 단순함은 궁극의 정교함이다
이 말은 레오나르도다빈치의 명언이자 Apple의 PC 광고에 사용된 문구입니다. “단순한 것은 복잡한 것보다 낫다. 단순하게 만들기 위해서는 많은 고민과 노력이 필요하다”는 의미입니다. 이 말을 먼저 소개하는 이유는 이번 글의 주제인 마이크로 프론트엔드 (Micro frontend) 또한 복잡한 것을 단순하게 만듦으로써 기존 개발방식의 단점을 보완하는 새로운 개발 방식이기 때문입니다.
마이크로 프론트엔드는 백엔드(Back-end, 사용자에게 드러나지 않는 DB 공간(회원가입, 로그인)) 개발 영역에서 보편화된 마이크로 서비스(Microservice, 벡엔드를 기능별로 나눠서 독립적으로 개발하고 관리하는 기술) 개념을 프론트엔드(Front-end, 최종사용자(End-User)가 원하는 기능을 작동시키는 앱 화면(검색창, 메뉴바, 버튼 등)) 영역에 적용한 개발 방식입니다. 마이크로 프론트엔드와 마이크로 서비스는 실천 방식이 동일하며, 획득하고자 하는 목표도 서로 닮아 있기 때문에 마이크로 서비스의 프론트엔드 확장 개념이라고 볼 수 있는데요. 다양한 앱이 결합된 슈퍼앱이 글로벌 트렌드가 되면서, 프론트엔드 영역에서도 마이크로 서비스를 적용하는 것이 각광받고 있습니다.
마이크로 프론트엔드의 특징
마이크로 프론트엔드와 비교되는 기존의 개발 방식에는 모놀리식(monolithic) 프론트엔드가 있습니다. 프로젝트의 전체 소스코드를 하나의 저장소에서 개발하고, 빌드와 배포까지 하나의 팀이 수행하는 개발 방식인데요. 아직까지 많은 프론트엔드 애플리케이션은 모놀리식 프론트엔드 방식으로 구현되고 있습니다. 반면 마이크로 프론트엔드는 ‘서로 독립된 형태의 프론트엔드 애플리케이션을 통합해 하나의 큰 프론트엔드 애플리케이션으로 구현하는 것’을 뜻합니다. 마이크로 프론트엔드의 가장 큰 특징은 ‘독립과 통합’이라고 할 수 있습니다.
• 독립
독립은 작게 나눈 애플리케이션(이하 모듈(Module, 앱을 하나의 기능 단위로 세분화한 것))이 다른 모듈에 영향을 받지 않고, 독립적으로 기능이나 화면을 제공하는 것을 의미합니다. 개발→빌드→배포로 이뤄지는 앱의 생명주기가 모듈별로 독립적인 상태가 되는 것인데요. 애플리케이션 코드의 독립뿐만 아니라 모듈을 개발하고 유지 보수하는 개발팀도 독립된 상태가 돼야 합니다.
• 통합
통합은 개발 과정에서 나눠진 모듈을 다시 조합해 더 큰 단위의 프론트엔드 애플리케이션을 구성하는 것을 의미합니다. 각 모듈은 이미 배포가 끝난 상태이므로, 통합 과정은 사용자가 애플리케이션을 사용하는 시점인 런타임에서 이뤄지는 것이 특징입니다.
마이크로 프론트엔드의 장단점
앞서 설명해 드린 작은 모듈을 만드는 ‘독립’과 런타임 환경에서의 ‘통합’이라는 마이크로 프론트엔드의 특징을 중심으로 장단점을 살펴보겠습니다.
• 장점
1) 한 팀에서 관리해야 하는 코드의 양이 적어지면서 코드의 복잡도가 낮아집니다. 개발과 유지 보수가 쉬워지고, 궁극적으로는 애플리케이션의 품질 향상을 기대할 수 있습니다.
2) 모듈로 분리된 환경에서는 각 모듈 개발에 필요한 인원만 개발 과정에 참여합니다. 즉, 개발자가 의사소통해야 하는 기획자, 디자이너, 백엔드 담당자의 규모가 축소되기 때문에 구성원 사이의 의사소통 비용이 감소합니다.
3) 하나의 팀이 담당하는 배포의 규모와 범위가 줄어듭니다. 개발에서 배포까지의 시간이 단축될 뿐만 아니라 모듈의 배포에 따른 전체 시스템에 대한 영향도도 줄어듭니다.
4) 모듈의 개발과 배포의 전 과정이 독립되므로 개발팀의 자유도는 높아집니다.
5) 각 모듈이 병렬적으로 업무를 수행할 수 있으므로 조직 전체의 개발 속도가 빨라집니다.
6) 런타임 환경에서 통합을 이루기 때문에 모듈에서 장애가 발생한 경우 장애가 발생한 모듈만 격리가 가능합니다. 이는 개별 모듈의 장애가 전체 애플리케이션의 장애로 확산하는 것을 차단합니다.
7) 모듈이 통합되는 형태를 자유롭게 조정할 수 있기에 사용자의 맥락에 맞는 화면을 제공합니다.
• 단점
1) 모듈별로 중복되는 코드가 많아지면 전체적인 리소스의 크기가 커질 수 있습니다. 이는 프론트엔드의 성능 저하의 원인이 될 수 있으므로 주의가 필요합니다.
2) 모놀리식 방식보다 초기 개발 환경 구성에 더 많은 시간이 필요합니다. 이는 분리와 통합을 위해 더 많은 설정과 개발 도구가 필요하기 때문입니다.
3) 모듈 통합, 모듈 간 통신, 사용자에 최적화된 화면 제공을 위한 코드가 추가되기 때문에 통합 과정에서의 복잡성이 높아질 수 있습니다.
4) 빌드타임 까지는 아무런 문제가 없다가 런타임 환경에서 모듈이 조합되면서 에러가 발생할 수도 있어 주의가 필요합니다.
5) UI/UX가 파편화되는 것을 막기 위해 통합된 UI/UX를 제공하기 위한 규칙이나 도구의 도입이 필요합니다.
마이크로 프론트엔드 전환, 꼭 필요할까?
마이크로 프론트엔드는 모놀리스 방식의 비효율적인 문제를 개선하는 해결책이 될 수 있지만, 만병통치약은 아닙니다.
만약 모놀리스 프론트엔드로도 서비스 사용에 문제없다면 마이크로 프론트엔드 전환 효용은 크지 않을 수 있습니다. 간단하고 작은 규모의 시스템이라면 모놀리식 프론트엔드가 더 적합한 구성입니다. 하지만 아래의 문제들이 발생되고 있다면 마이크로 프론트엔드 전환은 훌륭한 해결책이 될 수 있습니다.
첫째, 소스코드 규모가 비대해진 상황에서 특정 부분을 수정한 뒤, 예기치 못한 곳에서 문제의 발생 빈도가 높아질 때
→ 소스코드의 복잡도가 개발팀이 수용할 수 있는 한계에서 벗어나 소스코드에 대한 통제력을 상실한 상태입니다. 장애가 발생할 확률이 매우 높습니다. 하나의 팀이 관리하는 코드의 크기를 줄여 복잡도를 낮춰야 합니다.
둘째, 간단한 수정 사항 반영에도 개발부터 배포까지 긴 리드타임을 가질 때
→ 이 문제가 조직 전체의 생산성을 심각하게 해친다면 마이크로 프론트엔드를 고려해야 합니다.
셋째, 하나의 수정사항을 위해 수많은 구성원 간의 의사소통이 필요해 조직 전체의 업무 효율성을 해칠 때
→ 기획/디자인이나 백엔드 조직이 높은 수준으로 세분화되어 있어 의사소통 절차가 복잡한 상황이거나 프론트엔드 담당 조직이 상대적으로 빈약할 때 이러한 문제가 발생합니다. 의사소통에 필요한 절대적인 시간이 증가하면서 개발자가 개발/유지 보수에 신경 쓸 시간이 부족해진다면, 애플리케이션의 품질 문제로 이어질 수 있습니다.
LG CNS가 마이크로 프론트엔드를 구현해 내는 4가지 방법
LG CNS는 마이크로 프론트엔드 구현을 위해 다음과 같은 4가지 기술 활용을 권장합니다.
1. Module Federation(모듈 결합): javascript 기반의 런타임 통합을 구현하기 위한 모듈 결합
모듈 결합은 Webpack 5(모듈로 연결된 여러 개의 자바스크립트 파일을 하나로 합쳐주는 오픈 소스 기반의 모듈 번들러) 버전에서 구현된 개념으로 마이크로 프론트엔드 구현에 핵심적인 역할을 수행합니다. 모듈 결합을 활용하면 외부에 제공할 인터페이스 요소(버튼, 검색창 등)의 독립 배포가 가능합니다.
또한 런타임 환경에서 다른 모듈에 제공되는 기능을 끌어와 특정 모듈을 중심으로 통합할 수 있습니다. 모듈 결합 기반의 통합은 javascript의 동적 로딩(Dynamic Loading, 메모리를 효율적으로 관리하는 로딩 방식) 기능적으로 유사하며, 런타임 환경에서 모듈의 통합이 수행된다는 점이 차별화되어 있습니다.
2. Safe component: 장애 확산을 막기 위한 격리 조치
마이크로 프론트엔드 방식은 모놀리식 방식보다 통합 과정의 복잡도가 높아 관리가 어렵습니다. 따라서 하나의 프론트엔드 요소의 에러가 전체 시스템에 전파되지 않도록 방지하는 기능(Safe Component)은 선택이 아닌 필수입니다.
모던 UI 프레임워크는 에러의 확산을 막고 에러를 처리하기 위한 기능을 제공합니다. React.js(자바스크립트 라이브러리의 하나로서, 사용자 인터페이스를 만들기 위해 사용)에서는 Error Boundray(하위 컴포넌트 트리의 자바스크립트 에러를 기록하며 깨진 컴포넌트 트리 대신 폴백 UI를 보여주는 React 컴포넌트)와 UseErrorHandler Hook(비동기 코드, 서버 사이드 렌더링 등 이벤트 핸들러와 같은 범위 바깥의 에러 감지)을 활용해 런타임에 발생하는 에러에 대처할 수 있습니다.
3. Monorepo(Monolithic Repositories, 모노레포, 하나의 저장소에서 여러 개의 프로젝트를 관리하는 방식): 분리된 모듈과 공통 모듈의 효율적 관리
오늘날 코드의 저장소를 구성하는 방법에는 모노리스 (Monolith, 하나의 소프트웨어를 구성하는 모든 모듈을 한 프로젝트에서 관리하는 방식), 멀티레포 (Multi Repo, 저장소를 각 도메인 및 기능 시스템 단위로 생성해 운영하는 방식), 그리고 모노레포가 있습니다. 이 중 모노레포와 멀티레포 방식을 마이크로 프론트엔드 구현에 활용할 수 있습니다.
모노레포 방식은 독립된 모듈과 모듈 사이에 공유되는 공통 유틸, UI 컴포넌트를 편리하게 관리할 수 있습니다. 각 모듈 별 개발 환경 구성을 위한 설정들을 하나의 저장소 안에서 공유할 수 있기 때문에 초기 구축 비용을 줄일 수 있으며, 저장소 내 모듈의 전체 히스토리를 확인하기가 쉽다는 장점도 있습니다.
모듈의 독립성을 보장하는 관점에서 멀티레포 방식도 매력적인 대안입니다. 하지만 모노레포에 비해 공통 모듈을 추출하고 모듈의 버전을 관리해야 한다는 단점이 있습니다. 특히 새로운 모듈을 추가해야 할 경우 저장소를 새로 생성하고 재설정이 필요하기 때문에 저장소 관리를 위한 비용이 비교적 크게 발생합니다.
4. Design System: UI/UX 파편화 방지
마이크로 프론트엔드의 모듈별 개발팀은 서로 독립되어 있기 때문에 UI/UX 관점에서 파편화될 가능성이 있습니다. 이러한 현상을 막기 위해 디자인 시스템 도입을 권장하고 있습니다.
디자인 도구 ‘Figma’를 활용하면 디자인 컨셉과 가이드를 체계적으로 관리할 수 있습니다. 개발자에게 디자인을 공유하는 기능도 제공되기 때문에 기획자(혹은 디자이너)와 개발자 사이의 의사소통 비용이 감소합니다.
UI 개발도구 ‘Storybook’을 도입하면 UI/UX의 일관성을 유지하고, 의사소통 비용의 감소도 기대할 수 있습니다. 개발자는 UI 컴포넌트를 Storybook에 게시하는 것으로 동료 개발자들에게 자신의 개발 산출물을 공유할 수 있습니다. 기획자나 디자이너는 Storybook에 게시된 UI 컴포넌트와 개발 단계에서의 디자인 컨셉이 유지가 되고 있는지 쉽게 확인할 수 있습니다. 또한 UI 컴포넌트 단위로 개발자에게 피드백을 제공할 수 있습니다.
마이크로 프론트엔드는 코드와 시스템의 복잡도를 낮추고, 빌드 시간과 의사소통 비용을 줄여주며, 개발팀에게 높은 자율성을 부여합니다. 기존 모놀리식 프론트엔드의 문제점들을 보완하고, 지속 가능한 개발 환경을 구축할 수 있는 매력적인 대안이 될 수 있는데요. 하지만 도입을 위해서는 개발 방식이나 조직 문화에 많은 변화가 필요합니다.
개념 실증을 위한 간단한 규모의 마이크로 프론트엔드를 구현해 내는 것은 어렵지 않습니다. 모듈 결합을 이용해 화면 분할과 통합을 직접 구현해 볼 수 있을 것입니다. 여러분도 마이크로 프론트엔드를 직접 체험하고 새로운 아키텍처를 즐겨 보시기 바랍니다.
글 ㅣ LG CNS CTO 빌드센터 모바일서비스개발팀 이태희