올리브영 테크블로그 포스팅 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 2. 모듈 페더레이션 PoC
Tech

대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 2. 모듈 페더레이션 PoC

환경별 MF 구성 PoC 기록

2025.11.06

📖 목차

개요

안녕하세요, 올리브영 내 프론트엔드 개발을 담당하고 있는 노땅 문지훈🦊 입니다.


  1. 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 1. 마이크로프론트엔드 너 뭐야?
  2. 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 2. 모듈 페더레이션 PoC
  3. 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 3. Nx를 활용한 마이크로프론트엔드 (업로드 예정)

Part 1에서는 MFE(Micro Frontend)의 개념을 정리했습니다. 이제 구현으로 시선을 옮겨 보겠습니다. Part 2의 핵심은 모듈 페더레이션입니다. 레포지토리 전략과 무관하게, 런타임에 모듈을 공유해 MFE를 구현하는 아키텍처 패턴이죠. 이어지는 Part 3에서는 모듈 페더레이션을 더 쉽게 적용하도록 돕는 도구인 Nx를 다룰 예정입니다.

이번 글에서는 내부 PoC(Proof of Concept)를 통해 확인한 모듈 페더레이션의 핵심 개념, 장단점, 그리고 구현 방법을 차례로 살펴보겠습니다. 그럼 저와 같이 출발해 보실까요~ 🚀

모듈 페더레이션(Module Federation - MF) 넌 뭐니?

MF 로고
MF 로고

MF는 JavaScript 애플리케이션의 분산화를 위한 아키텍처 패턴입니다. (서버 측의 마이크로서비스와 유사) 이를 통해 여러 JavaScript 애플리케이션(또는 마이크로 프론트엔드) 간에 코드와 리소스를 공유할 수 있습니다.

MF를 도입해서 얻는 이점은 아래와 같습니다.

  • 코드 중복 감소
  • 코드 유지 관리성 개선
  • 애플리케이션의 전체 크기 감소
  • 애플리케이션 성능 향상

간단히 런타임에 여러 코드 및 리소스를 병합하는 기술이라고 이해하면 됩니다. 쉽죠잉?

A 애플리케이션 모듈을 수정한다고 가정해봅시다. 만약 빌드 타임에 통합하면, A를 사용하던 B, C 애플리케이션도 다시 빌드/배포해야 합니다. 그러면 마이크로프론트엔드의 장점인 독립적 배포가 어려워지죠. 반대로 런타임에 통합하면, B, C 애플리케이션에서 별도 배포 없이 A 애플리케이션의 변경 사항을 실시간 반영할 수 있습니다. 다시 말해 런타임에 각 서비스를 개별 배포하고, 동적으로 로딩할 수 있습니다.

자주 사용되는 개념

  • 호스트(Host) 애플리케이션: 다른 마이크로 프론트엔드를 로드하여 통합하는 주 애플리케이션입니다.
  • 리모트(Remote) 애플리케이션: 자신의 모듈을 외부에 노출하여 다른 애플리케이션에서 이를 사용할 수 있도록 하는 독립적인 마이크로 프론트엔드입니다.
  • 모듈: Webpack 또는 Vite로 번들링 가능한 리소스입니다. (JS, CSS, HTML, JSON, Asset ..)
  • Local: 현재 애플리케이션 내 단일 빌드에 포함되는 일반적인 모듈입니다.
  • Expose: 원격 모듈로 노출할 부분을 지정합니다. (expose하면 Host가 원격 모듈의 코드를 사용할 수 있게 됩니다.)
  • Container: 다른 애플리케이션에서 로드 가능한 단위입니다.
  • Shared Dependencies: 여러 애플리케이션(Host, Remote) 간에 공유할 의존성(라이브러리)입니다. MF는 설정된 공유 의존성을 버전까지 고려하여 중복 로드를 방지하고 효율적으로 관리해 줍니다. 예를 들어, Host와 Remote가 모두 React v18을 사용한다면, 브라우저에는 React v18 코드가 한 번만 로드됩니다. (Singleton 개념)

언제 사용할까?

MF는 다음 시나리오에 적합합니다.

  • 대규모 애플리케이션: 대규모 애플리케이션의 경우 애플리케이션을 여러 개의 마이크로 프론트엔드로 나누고 MF를 사용하여 이들 간에 코드와 리소스를 공유할 수 있습니다.
  • 마이크로 프론트엔드 아키텍처: MF는 마이크로 프론트엔드 아키텍처를 구축하는 데 이상적인 도구입니다.
  • 큰 규모의 개발팀: MF는 여러 팀이 대규모 애플리케이션을 공동으로 개발하는 데 도움을 줄 수 있습니다.

MAU 800-900만 명을 돌파하며 성장 중인 올리브영은 거대한 트래픽과 수많은 기능이 업데이트되는 환경인만큼, 기존의 모놀리식 프론트엔드 구조는 배포 병목, 코드베이스 복잡성 등 개발 속도를 저해하는 한계에 봉착했습니다.

이러한 문제 해결과 올리브영의 넥스트 비전 달성을 위해, 저희는 MF를 어떠한 구성 방식으로 사용하면 좋을 지 검토하였고, 이 구조적 문제를 해결할 핵심 기술인 MF의 Next.js 기반 PoC 과정과 결과를 이번에 공유하게 되었습니다.

이를 통해 해결하고자 하는 문제가 뭐야?

프로젝트 규모가 커지고 복잡해짐에 따라 다양한 문제에 직면하게 됩니다.

  • 중복 개발: 프로젝트가 커질수록 비슷한 기능이나 컴포넌트가 여러 부분에서 중복 구현되는 경우가 많아집니다.
  • 업무 결합도 증가: 프로젝트가 커질수록 하나의 팀이 아닌 다양한 팀이 하나의 코드베이스를 공유하게 되어 의존성 및 복잡성이 증가합니다. 또 팀 간 협업 과정에서 충돌과 병목현상이 발생합니다.
  • 응집도 낮음: 다양한 기능이 하나의 애플리케이션에 있다보니 응집도가 떨어집니다. 특정 기능을 수정하거나 확장할 때 전체 시스템에 대한 이해가 필요하여 코드 품질 관리가 어려워집니다.
  • 장애파급범위: 시스템의 한 부분에서 발생한 문제가 전체 애플리케이션에 영향을 미칠 수 있습니다. 또한 문제 발생 시 근본 원인을 찾아 해결하는데 비교적 많은 시간과 노력이 필요합니다.
  • 배포시간증가: 전체 애플리케이션을 한 번에 빌드하고 배포해야 하므로 시간이 오래 걸립니다. 작은 변경사항에도 전체 애플리케이션을 다시 배포해야 합니다.

이런 문제들을 해결하고자 하는 니즈는 누구나 가지고 있을 것입니다. 그 문제를 해결하는 여러 방법 중 하나가 MF라고 생각하면 될 것 같아요.

마이크로 프론트엔드에서 말하는 각 통합 방식 추천 환경

  • 빌드 통합 방식 추천 상황
    • 소규모 팀으로 단일 레포지토리에서 개발하는 경우
    • 성능과 SEO가 매우 중요한 경우
    • 기술 스택이 통일된 경우
  • 런타임 통합 방식 추천 상황
    • 여러 팀이 독립적으로 개발하는 경우
    • 독립적인 배포와 확장성이 중요한 경우
    • 다양한 기술 스택을 사용해야 하는 경우
    • 점진적인 마이그레이션이 필요한 경우

MF의 특징은 뭐가 있을까?

MF 런타임 통합
MF 런타임 통합

모놀리식과 차이점은?

일반적인 모놀리식과의 차이를 비교해봤습니다.

특징 모놀리식 프론트엔드 마이크로 프론트엔드
코드베이스 단일 거대 코드베이스 여러 개의 작은 코드베이스
개발 모든 팀이 동일 코드베이스 작업 팀별 독립적인 코드베이스 작업
배포 전체 애플리케이션 단위 배포 마이크로 앱 단위 독립 배포 가능
기술 스택 단일 기술 스택 (변경 어려움) 기술 스택 다양성 가능 (점진적 변경 용이)
팀 구조 기능별 팀 또는 거대 단일 팀 기능 중심 자율적 팀
결합도 높음 (변경 영향 범위 큼) 낮음 (변경 영향 범위 제한적)
복잡성 초기에는 낮으나, 규모 증가 시 급증 초기 설정 복잡성 높음, 개별 앱은 단순

MF를 사용하면 얻을수 있는 이점과 발생할 수 있는 이슈는?

이점

  • 개발 속도 향상 - 여러 팀이 동시에 독립적으로 작업할 수 있어 전체 개발 속도가 빨라집니다. 다른 팀의 작업을 기다릴 필요가 없습니다.
  • 유지보수성 향상 - 코드베이스가 작고 집중되어 있어 이해하기 쉽고 디버깅하기도 쉬워집니다. 거대한 코드베이스에서 버그 찾는 것보다 훨씬 효율적입니다.
  • 확장성 개선 - 새로운 기능이나 팀을 쉽게 추가할 수 있습니다. 기존 코드를 건드리지 않고 새 마이크로 프론트엔드를 추가하면 됩니다.
  • 기술 부채 관리 - 전체 애플리케이션을 한 번에 리팩토링할 필요 없이 한 번에 하나의 마이크로 프론트엔드를 현대화할 수 있습니다. "이번 분기에는 검색 기능만 React 18로 업그레이드하자!" 같은 접근이 가능합니다.
  • 장애 격리 - 한 마이크로 프론트엔드의 문제가 전체 애플리케이션에 영향을 미치지 않습니다. 결제 시스템에 버그가 있어도 사용자는 여전히 콘텐츠를 탐색할 수 있습니다.
  • 점진적 업그레이드 - 레거시 애플리케이션을 한 번에 하나씩 마이크로 프론트엔드로 전환할 수 있습니다. 빅뱅 방식의 리팩토링보다 훨씬 안전합니다.

발생할 수 있는 이슈

1. 성능 관련 이슈

  • 초기 로딩 시간 증가
    • 여러 마이크로 프론트엔드를 별도로 로드해야 하므로 초기 로딩 시간이 길어질 수 있습니다.
    • 여러 자바스크립트 번들을 다운로드하는 과정에서 네트워크 요청이 증가합니다.
  • 런타임 오버헤드
    • 여러 애플리케이션이 동시에 실행되면서 메모리 사용량이 증가할 수 있습니다.
    • 브라우저 리소스 경쟁으로 인한 성능 저하가 발생할 수 있습니다.

2. 의존성 관리 이슈

  • 공유 라이브러리 충돌
    • 여러 마이크로 프론트엔드가 다른 버전의 동일한 라이브러리를 사용할 경우 충돌이 발생할 수 있습니다.
    • 예를 들어, 한 마이크로 프론트엔드는 React 18을 사용하고 다른 하나는 React 17을 사용하면 문제가 발생합니다.
  • 전역 상태 관리 복잡성
    • 여러 마이크로 프론트엔드 간 상태 공유가 복잡해집니다.
    • Redux, Context API 등을 통한 상태 관리가 어려워질 수 있습니다.

3. 스타일 관련 이슈

  • CSS 충돌
    • 글로벌 CSS가 다른 마이크로 프론트엔드의 스타일을 덮어쓸 수 있습니다.
    • 클래스 이름 충돌이 발생할 수 있습니다.
  • 일관된 디자인 시스템 유지 어려움
    • 여러 팀이 독립적으로 개발할 경우 일관된 UI/UX를 유지하기 어려울 수 있습니다.
    • 테마 관리, 다크 모드 등의 전역 스타일 적용이 복잡해집니다.

4. 라우팅 관련 이슈

  • 라우팅 충돌
    • 각 마이크로 프론트엔드가 자체 라우터를 가질 경우 라우팅 충돌이 발생할 수 있습니다.
    • 브라우저 히스토리 관리가 복잡해집니다.
  • 딥 링킹 문제
    • 특정 상태나 페이지로 직접 이동하는 딥 링크 구현이 어려울 수 있습니다.
    • 브라우저 새로고침 시 상태가 유지되지 않을 수 있습니다.

5. 통신 관련 이슈

  • 컴포넌트 간 통신 복잡성
    • 마이크로 프론트엔드 간 이벤트 기반 통신이 복잡해질 수 있습니다.
    • 타입 안전성 보장이 어려울 수 있습니다.
  • API 중복 호출
    • 여러 마이크로 프론트엔드가 동일한 API를 중복 호출할 가능성이 있습니다.
    • 캐싱 전략이 복잡해질 수 있습니다.

6. 개발 및 배포 복잡성

  • 환경 구성 복잡성
    • 개발, 테스트, 스테이징, 프로덕션 환경 구성이 복잡해집니다.
    • 로컬 개발 환경 설정이 어려울 수 있습니다.
  • 배포 파이프라인 복잡성
    • 각 마이크로 프론트엔드의 배포 버전 관리가 복잡해집니다.
    • 롤백 전략 구현이 어려울 수 있습니다.

7. 테스트 관련 이슈

  • 통합 테스트 어려움
    • 마이크로 프론트엔드 간 통합 테스트가 어려워집니다.
    • E2E 테스트 구성이 복잡해질 수 있습니다.
  • 모킹 복잡성
    • 다른 마이크로 프론트엔드의 기능을 모킹하는 것이 복잡해질 수 있습니다.

8. SEO. 및 접근성 이슈

  • SEO 최적화 어려움
    • 클라이언트 사이드에서 동적으로 로드되는 마이크로 프론트엔드는 SEO 최적화가 어려울 수 있습니다.
    • 메타 태그 관리가 복잡해질 수 있습니다.
  • 접근성(A11y) 관리
    • 여러 팀이 개발할 경우 일관된 접근성 표준을 유지하기 어려울 수 있습니다.
    • 스크린 리더와 같은 보조 기술과의 호환성 문제가 발생할 수 있습니다.

MF를 구성하다보면 위와 같은 이슈들이 발생할 수 있기 때문에 MF를 구성하기 전 고려해야 할 부분들을 확실하게 고민해보고 설계하고 선택하면 좋을 것 같습니다.

고려해야 할 부분들

런타임 통합 방식의 마이크로 프론트엔드는 많은 이점을 제공하지만, 다양한 기술적 이슈가 발생할 수 있습니다. 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 3. Nx를 활용한 마이크로프론트엔드 에서 공유드릴 내용이기도 하지만 Nx를 활용하면 이러한 이슈들을 효과적으로 해결하면서 마이크로 프론트엔드의 장점을 활용할 수 있습니다.

해결책을 구현할 때는 다음과 같은 방법을 고려하세요.

  • 설계 단계부터 고려 - 마이크로 프론트엔드 경계, 통신 방식, 공유 리소스 등을 미리 설계
  • 팀 간 표준화 - 코딩 표준, 컴포넌트 설계 원칙, 스타일 가이드 등 공유
  • 점진적 도입 - 한 번에 모든 것을 마이크로 프론트엔드로 전환하기보다 점진적으로 도입
  • 모니터링 및 성능 최적화 - 지속적인 모니터링과 성능 개선

만약 Next.js를 사용 중이라면 놓치지 말아야 할 이슈가 있습니다. 현재 Page Router만 지원하며, 2026년에는 deprecated 처리될 예정입니다. 등록된 이슈를 참고해 구현하세요. 😭

이러한 접근 방식을 통해 런타임 통합 방식의 마이크로 프론트엔드를 성공적으로 구현하고 유지할 수 있습니다.

MF의 구현 방법은?

예제로 올리브영 웹에 Next.js 프레임워크의 두 가지 환경에서 모듈 페더레이션을 구현해보았습니다. 디플롯이라는 서비스에서 응답되는 상품에 대한 데이터를 올리브영 상품카드 컴포넌트를 리모트로 가져와서 병합 후 데이터를 주입하여 사용하는 케이스 예시입니다.

모든 코드는 모듈 페더레이션 GitHub을 참고해 테스트용으로 구성했습니다. (모듈페더레이션.io 예제 github)

Webpack 설정

적용 시나리오

MF 아키텍쳐
MF 아키텍쳐

호스트와 리모트의 개념을 아는게 중요합니다.

  • 최상위 Host APP: 라우팅 또는 상태를 관리하는 컨테이너가 최상단 Host 역할
  • HomeModule APP: 최상위 Host APP의 Remote 역할과 CommonUI의 Host 역할
  • CommonUI: HomeModule APP에서 사용하는 Remote App 역할

HomeModule은 CommonUI에서 제공하는 ProductCard 컴포넌트를 활용하여 페이지를 구성하고 최상위 Host에서 해당 HomeModule을 활용하여 페이지를 구성합니다.

Host

const NextFederationPlugin = require("@module-federation/nextjs-mf");

webpack(config, options) {
  config.module.rules.push(videoFileLoaderOptions);
  const { isServer } = options;
  const getRemotes = () => {
    const location = isServer ? "ssr" : "chunks";
    return {
      HomeModule: `HomeModule@http://localhost:3001/_next/static/${location}/remoteEntry.js`,
    };
  };
  config.plugins.push(
    new NextFederationPlugin({
      name: "host",
      remotes: getRemotes(),
      filename: "static/chunks/remoteEntry.js",
      exposes: {
      },
      extraOptions: {
        debug: true,
        automaticAsyncBoundary: true,
        enableImageLoaderFix: true,
        enableUrlLoaderFix: true,
      },
      shared: {
        // react-query의 의존성을 같이 공유해서 싱글톤 객체로 사용한다. 버전이 달라서 생기는 문제를 해소할 수 있다. (완전해소 아님)
        "@tanstack/react-query": {
          singleton: true,
          requiredVersion: false,
        },
        "@tanstack/core": {
          singleton: true,
          requiredVersion: false,
        },
      },
    })
  );
  return config;
},

Remote

HomeModule (CommonUI APP의 호스트이자 Host APP의 리모트)

const NextFederationPlugin = require("@module-federation/nextjs-mf");

webpack(config, options) {
  const plugins = [...config.plugins];
  const { isServer } = options;
  const getRemotes = () => {
    const location = isServer ? "ssr" : "chunks";
    return {
      host: `host@http://localhost:3000/_next/static/${location}/remoteEntry.js`,
	    CommonUI: `CommonUI@http://localhost:3002/_next/static/${location}/remoteEntry.js`,
    };
  };
  plugins.push(
    new NextFederationPlugin({
      name: 'HomeModule',
      filename: 'static/chunks/remoteEntry.js',
      exposes: {
        './home': './src/pages/home-module',
      },
      remotes: getRemotes(),
      extraOptions: {
        debug: true,
        exposePages: true,
        automaticAsyncBoundary: true,
        enableImageLoaderFix: true,
        enableUrlLoaderFix: true,
      },
      shared: {
        // react-query의 의존성을 같이 공유해서 싱글톤 객체로 사용한다. 버전이 달라서 생기는 문제를 해소할 수 있다. (완전해소 아님)
        "@tanstack/react-query": {
          singleton: true,
          requiredVersion: false,
        },
        "@tanstack/core": {
          singleton: true,
          requiredVersion: false,
        },
      },
    }),
  );
};

공통 컴포넌트

CommonUI

const NextFederationPlugin = require("@module-federation/nextjs-mf");

webpack(config, options) {
  const plugins = [...config.plugins];
  const { isServer } = options;
  const getRemotes = () => {
    const location = isServer ? "ssr" : "chunks";
    return {
      HomeModule: `HomeModule@http://localhost:3001/_next/static/${location}/remoteEntry.js`,
    };
  };
  plugins.push(
    new NextFederationPlugin({
      name: 'CommonUI',
      filename: 'static/chunks/remoteEntry.js',
      exposes: {
        "./ProductCard": "./src/ProductCard/index.tsx",
        "./ProductCards": "./src/ProductCards/index.tsx",
      },
      remotes: getRemotes(),
      extraOptions: {
        debug: true,
        exposePages: true,
        automaticAsyncBoundary: true,
        enableImageLoaderFix: true,
        enableUrlLoaderFix: true,
      },
      shared: {
        // react-query의 의존성을 같이 공유해서 싱글톤 객체로 사용한다. 버전이 달라서 생기는 문제를 해소할 수 있다. (완전해소 아님)
        "@tanstack/react-query": {
          singleton: true,
          requiredVersion: false,
        },
        "@tanstack/core": {
          singleton: true,
          requiredVersion: false,
        },
      },
    }),
  );
};

CSR

적용

첫 번째는 CSR 환경입니다. react 환경에서 사용할 수 있습니다.

HomeModule - CommonUI에 있는 ProductCard와 ProductCards를 런타임에 병합하여 가져옵니다.

import { lazy, Suspense } from "react";

import { Layout } from "@common/components";

const ProductCard = typeof window === "undefined" ? (): null => null : lazy(() => import("CommonUI/ProductCard"));
const ProductCards = typeof window === "undefined" ? (): null => null : lazy(() => import("CommonUI/ProductCards"));

const Page = () => {
  return (
    <Layout>
      <Suspense fallback={<div>로딩 중...</div>}>
        {
          Array.from({length: 20}).map((productProps, idx) => (
            <Anchor href={productTargetUrl}>
              <ProductCards layout="TYPE_3">
                <ProductCard {...productProps} />
              </ProductList>
            </Anchor>
          )
        }
      </Suspense>
    </Layout>
  );
};

export default Page;

Host

import { lazy, Suspense } from "react";

import { Layout } from "@common/components";

const HomeModule = typeof window === "undefined" ? (): null => null : lazy(() => import("HomeModule/home"));

const Page = () => {
  return (
    <Layout>
      <Suspense fallback={<div>로딩 중...</div>}>
        <HomeModule />
      </Suspense>
    </Layout>
  );
};

export default Page;

결과

MF CSR 결과 - 올리브영 앱에서 특정 페이지 일부를 MF 적용한 모습

CommonUI의 상품 카드 컴포넌트를 리모트로 가져와서 HomeModule 컴포넌트에 일부분에 적용하였습니다. CSR일 경우 Nextjs에서 응답한 HTML에 해당 컴포넌트에 대한 HTML이 첨부되지 않은 채로 응답됩니다.

SSR

적용

두 번째는 SSR 환경입니다. 예제 코드에서 보시다시피 nextjs 환경에 영향을 받습니다.

HomeModule - CommonUI에 있는 ProductCard와 ProductCards를 런타임에 병합하여 가져옵니다.

import { lazy, Suspense } from "react";
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query';

import { Layout } from "@common/components";

const ProductCard = typeof window === "undefined" ? (): null => null : lazy(() => import("CommonUI/ProductCard"));
const ProductCards = typeof window === "undefined" ? (): null => null : lazy(() => import("CommonUI/ProductCards"));

const Page = () => {
  // 예시
  const res = useQuery(queryOptions({
    queryKey: ['products'],
    queryFn
  }));

  return (
    <Layout>
      <Suspense fallback={<div>로딩 중...</div>}>
        {
          res.products.map((productProps, idx) => (
		        <Anchor href={productTargetUrl}>
              <ProductCards layout="TYPE_3">
                <ProductCard {...productProps} />
              </ProductList>
            </Anchor>
          )
        }
      </Suspense>
    </Layout>
  );
};

export default Page;

export async function getServerSideProps() {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery(queryOptions({
    queryKey: ['products'],
    queryFn,
    staleTime: 5000,
  }));
  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
}

Host

import type { GetServerSidePropsContext } from "next";
import dynamic from "next/dynamic";

import { Layout } from "@common/components";

const HomeModule = dynamic(() => import("HomeModule/home"), { ssr: true, loading: () => <p>Loading...</p> });

export default function Page(props: any) {
  return (
    <Layout>
      <ShopPage {...props} />
    </Layout>
  );
}
export async function getServerSideProps(context: GetServerSidePropsContext) {
  const Remote = await import("HomeModule/home");
  return Remote.getServerSideProps(context);
}

결과

MF SSR 결과 - 올리브영 앱에서 특정 페이지 일부를 MF 적용한 모습
MF SSR 결과 - 파싱된 HTML 결과

서버사이드 렌더링을 판단하는 기준은 Nextjs에서 응답된 HTML에 비동기로 받아온 데이터를 기준으로 HTML 내에 렌더한 엘리먼트 노드들이 첨부되어 있는 것을 확인하면 됩니다.

MF를 구성해보고 느낀 점은?

Module Federation 필요성과 원격 모듈 로딩 원리에 대해서 파악해 보았으나 아직은 사용하기에 불편한 점이 PoC간에 몇 가지 있었습니다.

원격 모듈을 추가할 때마다 웹팩 설정을 함께 수정해야 했습니다.

아주 큰 규모의 개발팀과 어플리케이션에 적합한 아키텍쳐라고 설명하고 있는데 만약에 수많은 모듈들과 함께 사용하게 된다면 expose를 계속적으로 수정해야 할텐데 이 부분이 파일명 오타 및 exposes 목록 내 누락등의 휴먼에러가 발생할 여지가 있어 이를 자동화 할 수 있는 방법이 필요하다고 생각했습니다.

원격 모듈을 추가할 때마다 호스트 어플리케이션에서 타입을 정의해 주어야 했습니다.

원격 모듈을 추가할 때마다 호스트 어플리케이션을 수정해야 했었고 이는 독립적 개발 및 배포라는 장점과 모순되는 부분이기도 했습니다. 또한 type safe 하게 설정해야 하는데 이를 자동화 할 수 있는 방법을 구현해 활용하지 못한게 많이 아쉬웠는데 이 부분을 자동화 한다면 원격모듈 추가가 쉬워질 것이라 생각했습니다.

SSR 환경의 모듈을 구현하기가 매우 어려웠습니다.

레퍼런스를 보니 왜 SPA 위주의 MF 예제만 있는지 이해할 수 있었습니다. Nextjs의 page router만 지원하는 것과 getServerSideProps 등을 가져와야 하는 부분들등이 구현하기 매우 까다로웠습니다.


이처럼 MF의 잠재력은 확실하지만, 대규모 프론트엔드 환경에서 수많은 모듈과 팀이 이 기술을 효율적이고 안전하게 활용하려면 PoC 과정에서 발견된 이 불편함들을 반드시 자동화하고 체계화해야 합니다. 과연 올리브영 프론트엔드 개발자들은 이 '수동 설정' 과 '타입 안전성' 이라는 난관을 어떻게 극복하고, Next.js 기반 MFE 아키텍처를 성공적으로 정착시키려고 할까요?

다음 Part 3에서는 이 문제들을 해결하기 위한 선택지, Nx를 활용한 올리브영의 MFE 아키텍처 구현 전략을 구체적으로 공개합니다. Part 3도 놓치지 마세요! 그리고 못다 한 이야기는 향후 이런 구성으로 풀어보겠습니다. 기대 많이 해주세요!

  • MF를 실제 운영 환경에 배포하고 모니터링 해본 경험
  • MF를 Type Safe하게 사용해보려 시도한 내용
  • Zephyr MF Devtools 활용한 경험

이상으로 MF PoC편을 마치도록 하겠습니다. 딱딱하고 긴 글이었는데 읽어주셔서 정말 감사드립니다.

참고 자료

Module FederationNextjsMicro Frontend
올리브영 테크 블로그 작성 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 2. 모듈 페더레이션 PoC
🦊
문지훈 |
Front-end Engineer
(노땅 프론트엔드 개발자) 아! 세월이여~~