올리브영 테크블로그 포스팅 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 3. Nx를 활용한 마이크로프론트엔드
Tech

대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 3. Nx를 활용한 마이크로프론트엔드

모노레포 도구인 Nx를 활용해서 마이크로프론트엔드 환경을 구축하는 방법을 알아봅니다.

2025.11.10

안녕하세요! 발레를 좋아하는 올리브영의 프론트엔드 개발자 개발레리나🩰 입니다.


마이크로프론트엔드(Micro Frontend Architecture,이하 MFE)는 하나의 거대한 애플리케이션을 작고 독립적인 모듈로 나누고, 각각의 모듈을 별도로 개발하고 운영할 수 있는 환경을 구축하는 아키텍처 패턴입니다.

이렇게 모듈을 분리하면 각 도메인이나 기능 단위로 코드를 관리할 수 있어 대규모 코드베이스에서도 리스크를 줄이고, 빠른 개발 속도를 유지할 수 있습니다.


만약 MFE에 대한 개념 자체가 생소하거나, 더 자세한 개념과 장단점이 궁금하시다면,

➡️ 시리즈 첫 번째 글인 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 1. 마이크로프론트엔드 너 뭐야? 먼저 읽고 오시는 걸 추천드려요 😊


MFE환경을 구축할 수 있는 대표적인 기술로는 Webpack을 활용한 모듈 페더레이션이 있습니다. 이 기술을 사용하면 서로 다른 앱을 런타임에 하나로 통합할 수 있게 되는데요,

이 기술에 대한 자세한 설명과 실제 구현 방식이 궁금하시다면,

➡️ 시리즈 두 번째 글인 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 2. 모듈 페더레이션 PoC 글을 참고해 주세요! 🦊


모듈 페더레이션은 멀티레포뿐 아니라 모노레포 환경에서도 구현할 수 있습니다. 특히 이번에 소개할 Nx간단한 명령어와 설정만으로 모듈 페더레이션을 손쉽게 적용할 수 있도록 지원합니다.

또한 모노레포의 특성상 런타임 통합뿐 아니라 빌드타임 통합도 가능하며, 공통 설정과 의존성 관리까지 체계적으로 구성할 수 있기 때문에 협업과 유지보수 측면에서도 큰 장점이 있습니다.


올리브영은 지속적인 서비스 성장과 함께 프론트엔드 코드베이스가 빠르게 확장되고 있습니다.
이에 따라 여러 도메인과 기능이 복잡하게 얽히면서, 각 모듈 간의 의존성을 명확히 파악하고 관리하는 것이 점점 더 중요해지고 있습니다. 또한 새로운 기능을 빠르게 출시해야 하는 비즈니스 요구사항과 함께, 각 팀이 독립적으로 개발하고 배포할 수 있는 유연성도 필요해지고 있습니다.


이런 상황에서 저희는 모노레포 환경에서 MFE를 구축할 수 있는 도구들을 검토하게 되었고, 그 과정에서 Nx를 실험하고 연구하게 되었습니다. Nx는 단순히 모노레포를 관리하는 도구를 넘어서, MFE와 같은 복잡한 아키텍처를 안정적으로 운영하는 데 필요한 기능들을 통합적으로 제공한다는 점에서 저희의 선택지가 되었습니다. (Nx 공식 사이트 바로가기)


이번 글에서는 ⌜대규모 프론트엔드 아키텍처의 새로운 패러다임⌟ 시리즈의 마지막 편으로, 올리브영이 왜 이 시점에 Nx를 고민하고 실험하게 되었는지 그 배경을 함께 살펴보고, Nx라는 모노레포 도구를 활용해 빌드타임과 런타임 환경에서 마이크로프론트엔드를 구현하는 방법을 실제 코드 예시와 함께 소개합니다.


📝 한 줄 요약 

Nx의 특징을 이해하고, 이 도구를 활용하여 빌드타임 통합 방식과 런타임 통합 방식으로 MFE를 구현하는 과정을 실제 코드로 실습하며 최종 결과물을 확인합니다.😁

빌드타입 통합 방식
런타임 통합 방식(w. 모듈 페더레이션)
런타임 통합 방식 결과 이미지

Nx가 무엇인가요?

Nx는 대규모 프로젝트에서 여러 앱과 라이브러리를 하나의 저장소에서 체계적으로 관리할 수 있도록 돕는 모노레포 도구입니다.

간단한 명령어를 통해서 특정 모듈의 변경을 감지하고, 변경이 일어난 모듈에 대해서만 명령을 실행하는 등의 기능을 제공합니다. 특히 remote caching을 지원하여 이전에 빌드된 모듈의 경우 빠르게 빌드 결과물을 불러올 수 있습니다.

또한 React, Next.js, Angular 같은 프레임워크를 위한 템플릿과 CLI를 제공하여 독립된 앱을 빠르게 구성하여 출시할 수 있게 됩니다.

주요 기능 요약

  • Computation Caching: 이전에 실행한 빌드/테스트 결과를 캐싱
  • Affected: 변경된 코드에만 영향을 받는 앱/라이브러리만 선택적으로 빌드/테스트
  • Graph: 앱과 라이브러리 간의 의존성을 시각화해 확인할 수 있음
  • CLI: Angular, React, Next.js, Express 등 개발 환경 구성 지원 → 초기 개발 비용 절감

왜 Nx인가요?

Nx는 모노레포 환경에서 MFE를 구축하기 위한 최적화된 구조를 제공합니다.

다만, 이러한 유사한 기능을 제공하는 다른 도구들도 있는데요, 그렇다면 다른 선택지들 중에서도 왜 Nx를 선택해야 할까요?

올리브영처럼 지속적으로 성장하는 플랫폼에서는 단순히 빌드 속도를 개선하는 것을 넘어서, 장기적인 관점에서 개발 생산성과 유지보수성을 극대화할 수 있는 도구가 필요합니다. 특히 복잡하게 얽히는 앱과 라이브러리 간의 의존성을 한눈에 파악할 수 있는 기능과, 새로운 기술 스택 도입 시 확장성이 뛰어난 플러그인 생태계는 대규모 코드베이스를 관리하는 데 필수적이라고 판단했습니다.

Nx와 유사한 기능을 제공하는 대표적인 도구인 Turborepo와 비교해보겠습니다.

Nx vs Turborepo

항목
Nx
Turborepo
의존성 시각화 지원 미지원
Affected 기능 지원 지원
Plugin 생태계 풍부함 (Next.js, React, Storybook 등) 상대적으로 제한적
초기 학습 난이도 다소 높음 낮음
설정 유연성 높음 보통

Turborepo도 강력한 모노레포 도구이지만, Nx는 MFE와 같은 복잡한 아키텍처를 구성할 때 더 많은 가이드와 기능을 제공합니다. 특히 특히 CLI 기반의 프로젝트 생성과 통합, 의존성 시각화와 같은 기능은 실무에서 개발 생산성을 크게 높여줍니다.

무엇보다도 Nx에서는 이 모든 구성 요소들은 프로젝트의 목적과 규모에 따라 점진적으로 도입할 수 있도록 각 기능이 모듈화된 구조로 설계 되어 있습니다. 그래서 작은 팀이든 대규모 조직이든, 필요에 따라 유연하게 적용할 수 있다는 점이 Nx의 큰 장점 중 하나입니다.

Nx 기반 모노레포에서 마이크로프론트엔드 환경 구축해보기

이제 실제 코드 예시를 통해 Nx에서 마이크로프론트엔드를 빌드타임과 런타임 방식으로 어떻게 구현할 수 있는지 살펴보겠습니다.

빌드타임 통합하기

빌드타임 통합은 여러 앱이나 모듈을 하나의 메인 앱 안에 포함시켜, 최종적으로 하나의 번들로 함께 빌드하고 배포하는 방식입니다. 


빌드타입 통합 구조도
빌드타임 코드 통합 시 MFE 구조도

1. 워크스페이스 생성하기

Nx에서 제공하는 CLI를 사용해 모노레포 워크스페이스 생성합니다.

npx create-nx-workspace@latest 

이 명령어를 실행하면 위 이미지와 같이 프롬프트에서 새로운 워크스페이스 구성을 위한 여러 설정들을 단계별로 선택할 수 있습니다. 


저는 nx-example이라는 이름으로 Nx 워크스페이스를 생성하였고, main-app이라는 이름의 Next.js 기반의 웹 어플리케이션을 초기 앱으로 설정하였습니다.

그 외의 ESLint, Prettier, 스타일 라이브러리나 테스팅 도구 등의 개발환경설정들도 자유롭게 선택하시면 됩니다.

모든 설정이 완료되면 아래와 같은 구조로 워크스페이스가 구성됩니다.



이제 아래 명령어를 입력하면 main-app을 작동시킬 수 있습니다.

npx nx g @nx/react:lib libs/sub-app

메인앱 구동 이미지
기본 메인앱 구동 화면

2. 메인 앱에 통합될 서브 모듈 생성하기

이제 libs 디렉토리에 메인 앱에 통합될 서브 모듈(라이브러리 앱)을 생성해보겠습니다. 

npx nx g @nx/react:lib libs/sub-app


저는 sub-app이라는 이름으로 React 기반의 라이브러리 앱을 생성하였습니다. 

위와 같이 설정을 완료하면 다음과 같이 libs 폴더 안에 sub-app이라는 새로운 라이브러리 앱이 추가됩니다.



명령어 두 줄만으로 하나의 Next.js 기반의 메인 앱과 React 기반의 서브 앱이 추가된 새로운 Nx 워크스페이스가 생성되었습니다!! 👍

이렇게 만들어진 Nx 워크스페이스의 주요한 폴더와 기능을 알아보겠습니다.

  • apps 폴더: 실제 실행 가능한 앱이 위치합니다.
  • libs 폴더: 여러 앱에서 공통으로 사용하는 공유 라이브러리나 서브 모듈을 정의합니다.
  • nx.json: Nx의 핵심 설정 파일로, 프로젝트의 구조와 스크립트 명령어 실행 규칙 등을 정의합니다.
  • tsconfig.base.json: 워크스페이스 전역에서 공유하는 TypeScript 설정 파일입니다.

3. 서브모듈을 메인앱에서 사용하기

이제 libs/sub-app에서 만든 모듈을 apps/main-app에서 가져와서 실제로 사용해보겠습니다.

sub-app에는 기본적으로 lib/sub-app.tsx라는 컴포넌트가 생성되어 있습니다. 이 컴포넌트를 불러오려면 @nx-example/sub-app 경로를 임포트하면 Nx에서 자동으로 경로를 매칭해줍니다.

// apps/main-app/pages/index.tsx
import { SubApp } from '@nx-example/sub-app';

export default function HomePage() {
  return (
    <div>
      <h1>
        <span> Hello there, </span>
        Welcome main-app 👋
      </h1>
      <SubApp />
    </div>
  );
}

이렇게 main-app 안에서 sub-app 내에 있는 컴포넌트를 불러오면 별다른 설정 필요없이 이 모듈은 빌드 시점에 main-app에 함께 통합됩니다.


빌드타임 통합 결과 이미지
빌드타임 통합 화면

현재 main-app과 sub-app은 서로 다른 디렉토리에 존재하고 각자 독립적인 구조를 가지고 있지만, 빌드 시점에 하나의 앱으로 통합되어 최종 번들에 함께 포함됩니다. 또한 Nx가 제공하는 Affected, 캐싱, 의존성 분석 기능을 함께 활용하면, 서브 모듈이 많아져도 전체 앱을 매번 재빌드할 필요 없이 변경된 부분만 빠르게 빌드할 수 있어 개발 환경에서 매우 효율적으로 사용할 수 있습니다.

그러나 이 방식은 모든 모듈이 하나의 배포 파이프라인을 따르기 때문에, 각 모듈이 독립적으로 배포되어 각 팀에서 자율적으로 배포 주기를 관리해야야하는 경우에는 적합하지 않습니다.


다음으로는 위와 같은 단점을 보완하여 앱을 완전히 분리된 단위로 개발하고 개별적으로 배포할 수 있는 런타임 통합 방식을 살펴보겠습니다.



런타임 통합하기 (feat. 모듈 페더레이션)

런타임 통합은 각 앱을 완전히 독립적으로 개발 및 배포한 뒤, 런타임 시점(어플리케이션 실행 시점)에 하나로 통합되어 서로 코드를 공유하는 방식입니다.


런타임 통합 구조도
런타임 코드 통합 시 MFE 구조도

1. 워크스페이스 생성하기

먼저 Nx CLI를 사용해 워크스페이스 생성합니다.

npx create-nx-workspace@latest 


runtimeExample이라는 workspace를 만들었습니다. 이 명령어를 통해 기본 앱이 생성이 되는데, 아래에서 host앱과 remote앱을 함께 생성하는 generator 명령어를 사용해 두 앱을 따로 만들어볼 예정입니다. 이 때 이미 생성된 기본 앱은 제거해주세요.



2. host앱과 remote 앱 추가하기

다음 명령어를 사용하면 모듈 페더레이션이 적용된 host/remote 앱을 동시에 생성할 수 있습니다.

물론 각각의 앱을 따로따로 만들어 모듈 페더레이션관련 설정들을 추가하는 방식으로도 구현이 가능하지만, 이렇게 만들면 좀 더 간편하게 host앱과 reomte 앱을 만들 수 있습니다.


npx nx g @nx/react:host apps/host --remotes=remote


⚠️ 주의

워크스페이스 이름이나 remote 앱 이름에 -, @ 같은 특수 문자가 포함되면 generator 실행 시 오류가 발생합니다.
예: "Invalid remote name: @nx-exmaple/remote-app"
특수 문자 폴더명 에러 화면
따라서 프로젝트나 워크스페이스의 이름에 특수기호가 들어가지 않도록 설정하세요!



그러면 이렇게 hostApp과 remoteApp이 동시에 생성됩니다.



이제 모듈 페더레이션설정 관련 파일들을 살펴보겠습니다.
각각의 앱 내부를 보면 module-federation.config.ts 파일이 자동으로 생성되어있습니다.


이 파일은 Webpack 모듈 페더레이션 설정을 Nx가 추상화하여 쉽게 사용할 수 있도록 제공하는 기능으로, 각 앱 간의 연결 관계와 노출할 컴포넌트를 정의하는 핵심 설정파일입니다.



3. 모듈 페더레이션 관련 설정 확인


<Host App>


Host 앱은 Remote 앱을 소비하는 쪽이기 때문에, remotes 항목을 통해 어떤 앱과 연결할지 명시해야 합니다.

// apps > hostApp > module-federation.config.ts

import { ModuleFederationConfig } from '@nx/module-federation';

const config: ModuleFederationConfig = {
name: 'hostApp',
/**
* To use a remote that does not exist in your current Nx Workspace
* You can use the tuple-syntax to define your remote
*
* remotes: [['my-external-remote', 'https://nx-angular-remote.netlify.app']]
*
* You _may_ need to add a `remotes.d.ts` file to your `src/` folder declaring the external remote for tsc, with the
* following content:
*
* declare module 'my-external-remote';
*
*/
remotes: ['remoteApp'],
};

/**
* Nx requires a default export of the config to allow correct resolution of the module federation graph.
**/
export default config;

  • name: 현재 host앱의 이름입니다.
  • remotes: 런타임 시점에 연결할 remote 앱의 이름입니다. 실제 remote앱의 이름과 일치해야합니다.

// apps > hostApp > src > app > app.tsx
import * as React from 'react';
import NxWelcome from './nx-welcome';
import { Link, Route, Routes } from 'react-router-dom';

const RemoteApp = React.lazy(() => import('remoteApp/AppComponent'));

export function App() {
  return (
    <React.Suspense fallback={null}>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/remote-app">RemoteApp</Link>
        </li>
      </ul>
      <Routes>
        <Route path="/" element={<NxWelcome title="hostApp" />} />
        <Route path="/remote-app" element={<RemoteApp />} />
      </Routes>
    </React.Suspense>
  );
}

export default App;

<RemoteApp>


Remote 앱은 자신의 내부 모듈 중 어떤 것을 외부에 공유할지 명시해야 하며, Host 앱은 이 이름을 기준으로 해당 모듈을 동적으로 불러옵니다.


// apps > hostApp > module-federation.config.ts

import { ModuleFederationConfig } from '@nx/module-federation';

const config: ModuleFederationConfig = {
  name: 'remoteApp',
  exposes: {
    './AppComponent': './src/app/app.tsx',
  },
};

/**
 * Nx requires a default export of the config to allow correct resolution of the module federation graph.
 **/
export default config;

  • name: 현재 remote 앱의 이름입니다. (host앱에서 이 이름을 통해 접근합니다.)
  • exposes: 외부에 노출할 컴포넌트 혹은 모듈 경로를 정의합니다.
    • 여기서AppComponent은 host에서 import할 때 사용되는 경로이고,
    • './src/app/app.tsx'은 실제 컴포넌트 파일의 위치입니다.

4. 타입 에러 방지를 위해 타입 선언 로직 추가 및 타입 설정 파일 수정

Host App에서 Remote App 모듈을 사용할 때 타입스크립트 에러를 방지하려면 아래와 같이 글로벌 타입 선언 파일을 추가해주세요.

// apps > host > src > remotes.d.ts
declare module "RemoteApp/AppComponent" {
  const App: React.ComponentType;
  export default App;
}

그리고 Host App이 Remote App의 코드를 인식하고 타입 컴파일을 할 수 있도록 tsconfig.app.json의 rootDir과 include를 수정합니다:


// apps > host > tsconfig.app.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "dist",
    "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo",
    "jsx": "react-jsx",
    "lib": ["dom"],
    "types": [
      "node",
      "@nx/react/typings/cssmodule.d.ts",
      "@nx/react/typings/image.d.ts"
    ],
    // 아래 로직 수정 루트 디렉토리에 remoteApp 패키지도 포함되도록 root 디렉토리 설정
    "rootDir": "../.." // "src"
  },
  "exclude": [
    "out-tsc",
    "dist",
    "src/**/*.spec.ts",
    "src/**/*.test.ts",
    "src/**/*.spec.tsx",
    "src/**/*.test.tsx",
    "src/**/*.spec.js",
    "src/**/*.test.js",
    "src/**/*.spec.jsx",
    "src/**/*.test.jsx",
    "jest.config.ts",
    "eslint.config.js",
    "eslint.config.cjs",
    "eslint.config.mjs"
  ],
  "include": [
    "src/**/*.js",
    "src/**/*.jsx",
    "src/**/*.ts",
    "src/**/*.tsx",
    // 아래 로직 추가 => remoteApp 패키지도 타입스크립트 컴파일 목록에 포함시키기
    "../../apps/remoteApp/src/**/*"
  ],
  "references": []
}

이제 Nx 명령어로 이 두 앱을 실행시켜보겠습니다! 🥁🥁

참고로 remote app을 실행하는 명령어를 입력하면, remoteApp과 함께 이를 참조하고있는 hostApp도 자동으로 실행됩니다.


nx serve remoteApp


콘솔에 명령어를 실행하면 위와 같이 두 앱이 병렬로 실행되고있는 것을 확인할 수 있습니다.

현재 각각의 앱은 다음과 같은 주소에서 접근 가능합니다.



각 주소에 접근해 보면 다음과 같이 각 앱이 실행되고 있는 모습을 확인할 수 있습니다.


호스트앱 실행 화면
호스트앱 실행 화면
리모트앱 실행 화면
리모트앱 실행 화면

그리고 아래와 같이 hostApp에서는 remoteApp에서 정의한 컴포넌트가 모듈 페더레이션을 통해 동적으로 로드되어 렌더링되는 것을 확인할 수 있습니다. 😍


런타임 통합 방식 결과 이미지
런타임 통합 방식 결과 화면

이렇게 런타임 통합 방식은 각 앱이 독립적으로 빌드 및 배포되면서도 동적으로 통합되어 하나의 어플리케이션처럼 동작할 수 있게 해줍니다. 

마치며

지금까지 Nx 기반의 모노레포 환경에서 마이크로프론트엔드를 빌드타임 방식, 런타임 방식으로 통합하는 방법을 알아보았습니다.

이 글을 작성하는 지금 시점에서도 몇 달 전과 비교해보면 설정 방식이나 CLI 명령어 등 사용법이 미묘하게 달라진 부분이 있더라구요...! 😲

그만큼 Nx가 얼마나 빠르게 변화하고 꾸준히 개선되고 있는지를 체감할 수 있었습니다. (빠르게 변화가 일어난다는 점은 커뮤니티와 수요가 그만큼 활발하다는 뜻이기도 하겠죠?)


Nx는 다소 러닝커브가 있지만, 그만큼 제공하는 기능이 다양하고 강력합니다. 빌드 캐싱, Affedted 기반 최적화, 앱 간의 의존성 그래프 시각화 등의 기능은 특히 규모가 큰 프로젝트일수록 유지보수와 협업 측면에서 큰 효과를 발휘할거라고 생각합니다. 

또한 단순히 모노레포 환경을 지원하는 도구를 넘어서, MFE 환경을 손쉽게 구축할 수 있도록 CLI 기반으로 모듈 페더레이션을 구현할 수 있도록 도와준다는 점에서 정말 좋은 도구라고 생각합니다.

물론 모든 팀과 모든 프로젝트에 정답이 될 수는 없겠지만, 이러한 도구가 어떤 문제를 해결할 수 있는지를 아는 것만으로도 앞으로 기술적인 문제를 해결해나가는 데 도움이 될 수 있다고 생각해요! 💪

이 시리즈가 마이크로프론트엔드 구조 도입을 고민하고있는 팀, 혹은 MFE에 궁금증을 가지고계셨던 프론트엔드 개발자분들에게 조금이나마 도움이 되었기를 바랍니다.


이번 글에서는 Nx를 활용한 MFE 환경 구축의 기본적인 방법을 다뤘지만, 실제 대규모 프로덕션 환경으로 확장하기 위해서는 더 많은 고민이 필요합니다. 상태 관리 전략, 공통 라이브러리 운영, 모니터링 및 디버깅, 성능 최적화 등 고도화된 과제들이 남아있죠. 올리브영 테크 팀은 이러한 영역들을 지속적으로 탐구하고 실전 경험을 쌓아가며, 그 과정에서 얻은 인사이트를 여러분과 함께 나누고자 합니다.

올리브영 테크 팀과 함께 성장하고 새로운 기술 패러다임을 만들어나가고 싶다면, 저희의 다음 글도 기대해주세요! 🚀


긴 글 읽어주셔서 감사합니다.

마이크로프론트엔드모노레포Nx
올리브영 테크 블로그 작성 대규모 프론트엔드 아키텍처의 새로운 패러다임 - Part 3. Nx를 활용한 마이크로프론트엔드
🩰
개발레리나 |
Front-end Engineer
프론트엔드 개발자 개발레리나입니다. 개발도 좋아하고 발레도 좋아해요 🫶