avatar
Published on

Webpack Module Federation ์ง์ ‘ํ•ด๋ณด๊ธฐ

Author
  • avatar
    Name
    yceffort

https://yceffort.kr/2020/09/webpack-module-federation ์—์„œ ์ด์–ด์ง„๋‹ค.

webpack 5๊ฐ€ ๋ฐœํ‘œ ๋˜๋ฉด์„œ ๋™์‹œ์— module federation๋„ ์ง์ ‘ํ•ด๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ํ•œ๋ฒˆ ์ง์ ‘ ์ ์šฉํ•ด๋ณด๋ฉด์„œ ์ •๋ง๋กœ ๊ฒŒ์ž„ ์ฒด์ธ์ €๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด์ž.

ํ•ด๋‹น ์˜ˆ์ œ ํ”„๋กœ์ ํŠธ ์ €์žฅ์†Œ๋Š” ์—ฌ๊ธฐ๋‹ค.

react v17๊ณผ webpack 5๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ, ์•„์ฃผ ๊ธฐ์ดˆ์ ์ธ ์„ธํŒ…๋งŒ ํ•ด์„œ ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•ด๋ณด์•˜๋‹ค.

main ์„ค์ •

์ผ๋‹จ ๋ฉ”์ธ ํ”„๋กœ์ ํŠธ๊ฐ€ ์žˆ๊ณ , ์—ฌ๊ธฐ์ €๊ธฐ์— ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ ธ๋‹ค ์“ฐ๋Š” ๋ชจ์Šต์„ ์ƒ์ƒํ•ด๋ณด๋ฉด์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž. main์€ module federation์œผ๋กœ ์„œ๋น™๋˜๋Š” ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐ€์ ธ๋‹ค๊ฐ€ ์“ฐ๋Š” federation์˜ ์ค‘์‹ฌ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

webpack.config.js

const path = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')
const { ModuleFederationPlugin } = require('webpack').container

module.exports = {
  entry: './src/index',
  mode: 'development',
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    port: 3001,
  },
  output: {
    publicPath: 'http://localhost:3001/',
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {
          presets: ['@babel/preset-react'],
        },
      },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'main',
      remotes: {
        app1: 'app1',
      },
      shared: ['react', 'react-dom'],
    }),
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
}

ModuleFederationPlugin์„ ์‚ฌ์šฉํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ContainerPlugin๊ณผ ContainerReferencePlugin ๋ฅผ ํ•ฉ์นœ ๊ฐœ๋…์ด๋ผ๊ณ  ๋ณด๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

์—ฌ๊ธฐ๋Š” ๋‹จ์ˆœํžˆ expose ํ•œ ๋‹ค๋ฅธ federation์„ ๊ฐ€์ ธ๋‹ค ์“ฐ๋Š” ์—ญํ• ๋งŒ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, exposes๋ฅผ ํ•˜๊ธฐ ์•Š๊ณ  ์žˆ๋‹ค.

app1 ์„ค์ •

main์—์„œ ๊ฐ€์ ธ๋‹ค ์“ธ ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ๋ฅผ exposeํ•˜๋Š” ๊ณณ์ด๋‹ค.

webpack.config.js

const path = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')
const { ModuleFederationPlugin } = require('webpack').container

module.exports = {
  entry: './src/index',
  mode: 'development',
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    port: 3002,
  },
  output: {
    publicPath: 'http://localhost:3002/',
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {
          presets: ['@babel/preset-react'],
        },
      },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      library: { type: 'var', name: 'app1' },
      filename: 'remoteEntry.js',
      exposes: {
        './Counter': './src/components/counter/index.jsx',
      },
      shared: ['react', 'react-dom'],
    }),
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
}

main๊ณผ ์ฐจ์ด์ ์€ exposes๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” ๊ฐ„๋‹จํ•œ Counter๋ฅผ ๋‚ด๋ณด๋‚ด๋„๋ก ํ•˜๊ณ  ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋ ‡๊ฒŒ ๋‚ด๋ณด๋‚ธ Counter๋ฅผ https://localhost:3000/remoteEntry.js์—์„œ ์„œ๋น„์Šค ํ•˜๋„๋ก ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

main

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Main App</title>
  </head>

  <body>
    <div id="root"></div>
    <script src="http://localhost:3002/remoteEntry.js"></script>
  </body>
</html>

์•„๊นŒ ์„œ๋น™ํ•˜๊ธฐ๋กœ ์ž‘์„ฑํ•ด๋‘” remoteEntry.js๋ฅผ ๋•ก๊ฒจ์˜ค๋Š” ๋ชจ์Šต์ด๋‹ค. ๋ฌผ๋ก  ๋” ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” async ๋“ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ๋„ ์žˆ๋‹ค.

bootstrap.js

์ด๋ฆ„์ด bootstrap์ธ ์ด์œ ๋Š” ๊ณต์‹ ๋ฌธ์„œ์—์„œ ๊ทธ๋ ‡๊ฒŒ ํ•˜๊ณ  ์žˆ๊ธธ๋ž˜ ๊ทธ๋ ‡๊ฒŒ ํ–ˆ๋‹ค. ๐Ÿ‘€ ๋œป๊ณผ๋„ ์—ฐ๊ด€์ด ์žˆ์„๋“ฏ.

import React, { Suspense } from 'react'
import ReactDOM from 'react-dom'

const Counter = React.lazy(() => import('app1/Counter'))

function App() {
  return (
    <>
      <h1>Hello from React component</h1>
      <Suspense fallback="Loading Counter...">
        <Counter title={'hello, counter'} />
      </Suspense>
    </>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

React์˜ lazy์™€ suspense๋ฅผ ํ™œ์šฉํ•˜์—ฌ app1์—์„œ exposeํ•œ Counter๋ฅผ ๊ฐ€์ ธ๋‹ค ์“ฐ๊ณ  ์žˆ๋‹ค.

๊ฒฐ๊ณผ

์นด์šดํ„ฐ๊ฐ€ ์ž˜ ๋‚˜์˜ค๊ณ  ์žˆ๊ณ 

result1

์ •์ƒ์ ์œผ๋กœ remoteEntry์—์„œ ๊ฐ€์ ธ๋‹ค ์“ฐ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

result2

๊ทธ๋ฆฌ๊ณ  ๋‘ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋‘ share๋กœ ['react', 'react-dom']์„ ์“ฐ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, ์ด๊ฒƒ ์—ญ์‹œ ์ค‘๋ณต๋˜์ง€ ์•Š๊ณ  main์—์„œ ๋ฌถ์–ด์„œ ์“ฐ๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

์ข‹์€ ์  ๋‚ด์ง€๋Š” ๊ธฐ๋Œ€ํ•˜๋Š” ๋ฏธ๋ž˜

์š”์ฆ˜ ์œ ํ–‰์ด๋ผ๊ณ  ํ•˜๋Š” Micro Frontend๋ฅผ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜ ์ธ ๊ฒƒ ๊ฐ™๋‹ค. ํ•˜๋‚˜์˜ ์•ฑ์ด ๋ฉ์น˜๊ฐ€ ๋„ˆ๋ฌด ์ปค์„œ, ์‹ฑ๊ธ€ ํดํŠธ์˜ ์œ„ํ—˜ ๋‚ด์ง€๋Š” ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ ์“ธ ๋ฐ ์—†์ด ๋‹ค ๋ถˆ๋Ÿฌ์™€์•ผ ํ•˜๋Š” ๋ฌธ์ œ ๋“ฑ๋“ฑ์ด ์กด์žฌํ•˜๋Š”๋ฐ, module federation์ด ๊ทธ๊ฒƒ์„ ํ›Œ๋ฅญํ•˜๊ฒŒ ํ•ด๊ฒฐํ•ด ์ค„ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. (๋ฌผ๋ก  main์ด ๊ณ ์žฅ๋‚˜๋ฒ„๋ฆฌ๋ฉด ๋‹ต์ด ์—†๊ฒ ์ง€๋งŒ)

https://micro-frontends.org/ressources/diagrams/organisational/verticals-headline.png

์•„์‰ฌ์šด ์ 

๋ฌธ์„œ๊ฐ€ ์ž˜ ๋‚˜์™€์žˆ์œผ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์€๋ฐ ์•„์ง webpack์˜ ๋ฌธ์„œ๊ฐ€ ์ข€ ๋ถ€์‹คํ•œ ๊ฒƒ ๊ฐ™๋‹ค.

๊ทธ๋ž˜์„œ

๋ฅผ ๊ทธ๋ƒฅ ์ฐธ๊ณ  ํ•˜๋ฉด์„œ ํ–ˆ๋‹ค. ๋‹ค๋ฅธ ์—ฌํƒ€ ๊ธฐ๋Šฅ๋“ค ์ฒ˜๋Ÿผ webpack document์—์„œ ์˜ต์…˜์œผ๋กœ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋Š” object์˜ ํŠน์ง•์ด๋‚˜ ๊ฐ’์„ ๋ช…์‹œํ•ด์ฃผ์—ˆ์œผ๋ฉด ์ข‹๊ฒ ๋‹ค.

https://webpack.js.org/concepts/module-federation/#containerplugin-low-level

์•„์ง์€ ๋ฌธ์„œ๊ฐ€ ๊ทธ๋ƒฅ ์•„์ฃผ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ์™€ ์ปจ์…‰์ •๋„๋งŒ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ์–ด์„œ ์•„์‰ฝ๋‹ค.

์ด๋Ÿฌํ•œ Documentation์˜ ์•„์‰ฌ์›€ ๋ง๊ณ ๋Š” ์•„์ง ์ด๋ ‡๋‹คํ•  ๋‹จ์ ์„ ๋Š๋ผ์ง€ ๋ชปํ–ˆ๋‹ค. (๋ฌผ๋ก  ๋Œ€๊ทœ๋ชจ ์„œ๋น„์Šค์— ์ง์ ‘ ์จ๋ณด์ง€๋Š” ์•Š์•˜์ง€๋งŒ) ํ–ฅ ํ›„์— create-react-app์ด๋ผ๋“ ์ง€, ๋‹ค๋ฅธ ํ”„๋ก ํŠธ์—”๋“œ ์ƒํƒœ๊ณ„์—์„œ ์ ๊ทน์ ์œผ๋กœ ์‚ฌ์šฉ๋˜์–ด์„œ ๋”์šฑ ๋ฐœ์ „ํ•ด๋‚˜๊ฐ”์œผ๋ฉด ์ข‹๊ฒ ๋‹ค.

๋‹ค์–‘ํ•œ ์˜ˆ์ œ๋“ค

๋”์šฑ ๋‹ค์–‘ํ•œ ์˜ˆ์ œ๋“ค์€ ์—ฌ๊ธฐ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.