모듈 페더레이션이란 Zack jackson이 개발한 javascript의 아키텍처입니다. 이 아키텍처를 사용하면 두 개의 서로 다른 애플리케이션 코드베이스 간에 코드와 종속성을 공유할 수 있습니다.
다시 말해 모놀리식 아키텍처의 문제점을 해결할 수 있습니다.
서로 상당히 의존하고 있기 때문에 feature들 사이에 문제가 발생했을 때, 이를 의존하고 있는 다른 feature 또한 오류를 발생시킵니다.
또 만약 새로운 것을 추가 혹은 수정을 하면 의존성을 가지는 다른 feature에 오류를 불러 일으킬 수 있습니다.
뿐만 아니라 빌드 및 배포에도 어려움이 있습니다. 만약 이미 배포한 프로덕에 조그마한 수정사항이 있어 수정했다면 다시 그것의 전체를 빌드하고 배포시켜야합니다. 이렇게되면 많은 시간을 낭비하게됩니다.
또 서로 다른 피처들을 개발하고 배포하기전 머지시킬 때 또한 충돌로 인한 문제가 발생 할 수도 있습니다.
마이크로 프론트 엔드란 백엔드에서 사용하고 있는 마이크로 서비스 아키텍처 처럼 프론트엔드에서 관리하는 서비스를 분리해서 개발, 관리하는 패턴을 말합니다.
서술해보자면 하나의 서비스는 한개의 스크럼 팀에서 개발하고 운영됩니다. 그들은 독립적으로 그들의 서비스를 개발합니다. 그리고 최종적으로 각 팀에서 개발된 개별 어플리케이션은 하나의 프로덕트로 합쳐지게 되는 것입니다. 이렇게 되면 개발자 입장에선 각각의 독립적인 개체이지만 사용자는 하나의 프로덕트로 보게 되는 것입니다.
웹 애플리케이션을 구축하기 위해 마이크로 프론트엔드 접근 방식을 채택하는 것이 좋은 선택입니다. 특히 이는 변화는 부분이 많은 대규모 앱을 구축하는 경우 그렇습니다.
장점들이 처음 읽었을 때 와닿지 않을 것입니다. 이는 지금부터 설명하는 글을 읽다보면 자연스럽게 이해하실 수 있을 것입니다.
간단한 예제를 통해 module federation을 이해해봅시다.
mkdir federation
폴어를 만들고 header 프로젝트를 생성하겠습니다.
npx create-react-app header
헤더에 폴더구조는 다음과 같습니다.
index.js에 내용을 다음과 같이 boostrap.js 파일로 옮깁니다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
import('./bootstrap');
자유롭게
import React from 'react';
const Header = () => {
return (
<div>
Header입니다.
</div>
);
};
export default Header;
const { ModuleFederationPlugin } = require('webpack').container;
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExternalTemplateRemotesPlugin = require('external-remotes-plugin');
const path = require('path');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
entry: './src/index',
mode: 'development',
devtool: 'source-map', // 디버깅 과정 향상 빌드 및 리빌드 속도에 큰 영향을 미침, 가장 느린 거
optimization: {
minimize: false,
},
devServer: {
hot: true,
static: path.join(__dirname, 'dist'),
port: 3001,
liveReload: false,
},
output: {
publicPath: 'auto',
clean: true,
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/preset-react'],
plugins: [require.resolve('react-refresh/babel')],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'header',
filename: 'remoteEntry.js',
exposes: {
"./Header": "./src/Header.js",
},
}),
new ExternalTemplateRemotesPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
chunks: ['main'],
}),
new ReactRefreshWebpackPlugin({
exclude: [/node_modules/, /bootstrap\.js$/],
}),
],
}
webpack.config.js 파일이 이해가 되지 않을 수 있습니다.
이들을 모두 설명하기에는 너무 많은 내용이므로, plugin에 ModuleFederationPllugin만 설명하겠습니다.
사실 이렇게 보면 3개의 차이를 못 느끼실 것입니다.
그렇지만 header를 받아들이는 frame 코드를 보면 3개의 차이를 느낄 수 있을 것입니다.
index.js와 bootstrap.js는 똑같이 작성합니다.
import React from "react";
const Header = React.lazy(() => import("header/Header"));
const App = () => {
return (
<div>
<Header />
</div>
);
};
export default App;
const { ModuleFederationPlugin } = require('webpack').container;
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExternalTemplateRemotesPlugin = require('external-remotes-plugin');
const path = require('path');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
entry: './src/index',
mode: 'development',
devtool: 'source-map', // 디버깅 과정 향상 빌드 및 리빌드 속도에 큰 영향을 미침, 가장 느린 거
optimization: {
minimize: false,
},
devServer: {
hot: true,
static: path.join(__dirname, 'dist'),
port: 3000,
liveReload: false,
},
output: {
publicPath: 'auto',
clean: true,
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/preset-react'],
plugins: [require.resolve('react-refresh/babel')],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'frame',
remotes: {
header: 'header@http://localhost:3001/remoteEntry.js',
},
}),
new ExternalTemplateRemotesPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
chunks: ['main'],
}),
new ReactRefreshWebpackPlugin({
exclude: [/node_modules/, /bootstrap\.js$/],
}),
],
}