말랑말랑제리스타일

Webpack을 이용한 마이크로 프론트엔드 구현 본문

프로그래밍/웹코딩(html&JS)

Webpack을 이용한 마이크로 프론트엔드 구현

제리제리 2024. 7. 9. 14:50

Webpack의 Module Federation Plugin을 이용한 마이크로 프론트엔드 서비스를 구현해 봤습니다.

간단한 컨테이너 앱에 마이크로 앱 두 개를 보여주는 형식으로 만들었기 때문에 Webpack의 Module Federation Plugin을 이용한 마이크로 프론트엔드 구현 방식을 이해하는 목적으로 사용해 주시면 좋을 것 같네요.

Webpack과 마이크로 프론트엔드란?

자 일단 마이크로 프론트엔드와 Webpack이 뭔지부터 간단히 이해를 해보죠.

먼저 마이크로 프론트엔드는 프론트엔드의 소스를 여러 개로 분할해서 서비스하는 방식입니다.

여러 사람이 같이 운영하는 프론트엔드 화면의 개발에 상당히 유용하겠죠.

Webpack은 자바스크립트 모듈 중 하나로 여러 개의 파일을 하나로 병합해서 컴파일해 주는 모듈입니다.

그리고 Webpack의 플러그인 중 하나인 Module Federation Plugin은 각각의 다른 곳에 위치한 소스를 사용할 수 있게 해주는 플러그인이죠.

즉, Module Federation Plugin은 마이크로 프론트엔드 구현에 상당히 유용한 Webpack의 기능이란 거죠.

 

Webpack으로 마이크로 프론트엔드 구현을 위해 준비해야될 것

Webpack을 이용한 마이크로 프론트엔드 구현에 필요한 건 별거 없습니다.

VS Code가 있어야 되고, node js를 설치해야 합니다. npm을 사용할 것이기 때문이죠.

사실 VS Code는 없어도 동작은 될 텐데 있는 게 편하니까 설치합시다.

 

Webpack을 이용한 마이크로 프론트엔드 개발 실습

자 이제 본격적으로 Webpack을 이용해 마이크로 프론트엔드 서비스를 개발해 봅시다.

1단계 첫 번째 마이크로 서비스 개발

실제로는 컨테이너 앱을 먼저 만드는 상황이 많겠지만 우선 별거 없는 마이크로 서비스를 먼저 만들어봅시다.

프로젝트용 폴더로 "webpack-mfe"를 만들고 VS Code에서 이 폴더를 열어줍니다.

webpack-mfe 폴더 하단에 "microapp1"이라는 폴더를 만들어줍니다.

메뉴 바의 Terminal-new Terminal을 클릭해서 터미널을 하나 열어주고요.

cd microapp1

터미널에 이렇게 입력해서 microapp1 폴더로 이동합니다.

npm init -y

다음으로 상단의 "npm init" 명령어를 입력해서 package.json 파일을 생성해 줍니다.

package.json 파일이 생성된 게 보이실 겁니다.

이제 웹팩 사용을 위한 패키지 몇 개를 npm으로 설치해 줄 겁니다.

npm install webpack@5.4.0 webpack-cli@4.2.0 webpack-dev-server@3.11.0 html-webpack-plugin@4.5.0

버전을 직접 지정해 준 이유는 이 버전이 안 맞는 경우 에러가 발생할 수 있기 때문입니다.

참고로 , Module Federation Plugin은 webpack 5 버전 이후부터 지원이 됩니다.

webpack dev server 3.11.0 버전에서 보안 에러 비슷한 게 나올 수 있는데 가볍게 무시해 줍시다.

이걸 올렸더니 cli와 버전 호환이 떨어져서 에러가 나는 걸 확인해서 저는 건들지 않았습니다.

 

자 여기까지 완료되었으면 package.json 파일이 변경이 되고 microapp1 폴더 밑에 node_modules 폴더와 package-lock.json 파일이 생성된 것을 확인할 수 있을 겁니다.

microapp1 폴더 하단에 src 폴더와 public 폴더를 생성해 줍시다.

그리고 src 폴더 하단에 "index.js"를, public 폴더 하단에 "index.html" 파일을 생성해 줍니다.

마지막으로 "microapp1" 폴더 하단에 "webpack.config.js" 파일을 생성해 줍니다.

microapp1의 프로젝트 구조
microapp1의 프로젝트 구조

microapp1의 프로젝트 구조는 이렇게 되겠죠.

참고로 방금 생성한 javascript 파일과 html 파일은 비어있으면 됩니다.

 

자 이제 코딩을 좀 해봅시다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Microapp 1</title>
  </head>
  <body>
    <h1>Microapp 1</h1>
    <div id = "micro-app-1"></div>
  </body>
</html>

index.html 파일

여기에 microapp1이라고 id를 지정한 div에 javascript로 내용을 전달할 겁니다.

뭐 별건 없죠.

let hellostring = 'Hello from microapp1!';

document.querySelector('#micro-app-1').innerHTML = hellostring;

index.js 역시 간단하게 "micro-app-1"을 찾아서 문자열을 하나 넣어주는 것 외에는 특별한 게 없는 소스로 코딩해 줍니다.

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'development',
    devServer: {
        port: 8081,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html',
        }),
    ],
};

다음으로 webpack.config.js 파일입니다.

Html Webpack Plugin을 가져오고 mode는 로컬에서 작동시킬 것으므로 development로 해줍니다.

포트는 메인 컨테이너를 8080에서 동작시킬 것이기 때문에 8081로 지정해 주고 Html Webpack Plugin의 템플릿은 public폴더의 "index.html" 파일을 바라보도록 해줍시다.

 

마지막으로 package.json 파일을 열어보면 script라고 되어있는 부분이 보일 겁니다.

{
  "name": "microapp1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack serve"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "html-webpack-plugin": "^4.5.0",
    "webpack": "^5.4.0",
    "webpack-cli": "^4.2.0",
    "webpack-dev-server": "^3.11.0"
  }
}

package.json 파일내부 script의 test를 지우고 "start : webpack serve"를 넣어서 이런 식으로 수정해 줍시다.

Webpack-cli 타입 에러 발생

에러가 발생하지 않는 분들은 이 부분을 생략하고 넘어가도 됩니다.

그러나 저는 에러가 발생했네요.

에러 내용 : "TypeError: Class constructor ServeCommand cannot be invoked without 'new'"

webpack cli 타입 에러 발생 화면
webpack cli 타입 에러 발생 화면

에러 내용을 보면 new라는 커맨드를 cli에서 사용할 수 없다는 것으로 보입니다.

webpack cli 버전을 올려줍시다.

지금 npm으로 설치한 버전이 4.2.0 버전인데요.

 

webpack-cli

CLI for webpack & friends. Latest version: 5.1.4, last published: a year ago. Start using webpack-cli in your project by running `npm i webpack-cli`. There are 7699 other projects in the npm registry using webpack-cli.

www.npmjs.com

여기 들어가서 보니 다음 버전이 4.3.0이네요.

극단적으로 최신 버전으로 올리면 또 다른 에러가 발생할 수 있으니 하나씩만 올려봅니다.

npm install webpack-cli@4.3.0

올렸지만 또 다른 에러가 발생합니다.

에러 내용 : "TypeError: cli.toKebabCase is not a function"

이런 에러가 발생했는데요.

npm install webpack-cli@4.4.0

이번에도 한 단계 올려 4.4.0 버전을 설치해 줍시다.

이렇게 올리다 보니 마침내 4.10.0에서 webpack cli 에러가 사라집니다.

그러나 이번에는 http parser 관련 에러가 발생하네요

에러 내용 : Access to process.binding('http_parser') is deprecated

html webpack plugin 버전을 올려줍시다.

 

html-webpack-plugin

Simplifies creation of HTML files to serve your webpack bundles. Latest version: 5.6.0, last published: 7 months ago. Start using html-webpack-plugin in your project by running `npm i html-webpack-plugin`. There are no other projects in the npm registry us

www.npmjs.com

 

4.5.0 다음 버전이 5.0.0이네요.

5.0.0으로 올려주고 다시 "npm run start"를 입력하면 정상적으로 localhost 8081 포트에서 동작을 합니다.

 

다시 본론으로 돌아와서 에러가 발생하지 않는다면 local host 8081 포트로 접속 시 이런 화면이 나올 겁니다.

micro app 1의 동작 화면
micro app 1의 동작 화면

index.html 파일에서 "Microapp 1" 문구를 가져왔고 index.js 파일에서 "Hello from microapp1"이라는 문구를 가져왔죠.

 

2단계 두 번째 마이크로 서비스 개발

이어서 두번째 마이크로 서비스도 만들어보겠습니다.

microapp2 폴더를 만들어주고 microapp1에 있는 package.json 파일을 붙여 넣어줍니다.

그리고 name만 microapp2로 바꿔줍시다.

메뉴 바의 Terminal - New Terminal을 하던지 Ctrl + Shift + 백틱(1 왼쪽에 있는 키) 단축키로 터미널을 하나 더 띄워줍니다.

cd microapp2

명령어로 microapp2로 이동해 주고요

npm i

명령어로 초기화해줍니다.

3개 4개 만들고 싶으면 이렇게 복사해 나가면 됩니다.

마찬가지로 public 폴더 생성 후 하단에 index.html 파일을 만들고 src 폴더 생성 후 하단에 index.js 파일을 생성합니다.

마지막으로 webpack.config.js 파일 역시 생성해 줍니다.

microapp2까지 생성한 프로젝트 구조 하이라키입니다.
microapp2까지 생성한 프로젝트 구조

소스 코드는 대충 문구만 바꿔서 복사해 줍니다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Microapp 2</title>
  </head>
  <body>
    <h1>Microapp 2</h1>
    <div id = "micro-app-2"></div>
  </body>
</html>

microapp2의 index.html 파일

let hellostring = 'Hello from microapp2!';

document.querySelector('#micro-app-2').innerHTML = hellostring;

microapp2의 index.js 파일

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'development',
    devServer: {
        port: 8082,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html',
        }),
    ],
};

webpack.config.js 파일 역시 포트만 8082로 바꿔줍니다.

모두모두 저장했다면 "npm run start" 명령으로 실행해 봅시다.

localhost 8081에는 microapp1이, 8082에는 microapp2가 실행 중인 것을 확인할 수 있습니다.

 

3번째, 4번째 마이크로 앱 역시 이런 방식으로 생성할 수 있습니다.

3단계 컨테이너 서비스 개발

이번에는 컨테이너 서비스를 만들고 Module Federation Plugin을 적용시켜 마이크로앱을 컨테이너 앱 위에 띄우도록 해보겠습니다.

webpack-mfe 폴더 밑에 container 폴더를 생성합니다.

그리고 마찬가지로 microapp1에 있는 package.json 파일을 복제해 줍니다.

마찬가지로 name은 container로 바꿔줍니다.

그리고 마찬가지로 터미널을 하나 열고 container 앱으로 이동한 뒤 "npm i"를 실행해 줍니다.

여기서 추가로 설치해야 될 게 있는데요.

npm install nodemon

명령어로 nodemon이라는 패키지를 설치해 줍시다.

저 같은 경우 최신 버전인 3.1.4가 설치되었네요.

 

설치했다면 마찬가지로 src 폴더, index.js 파일, public 폴더, index.html 파일, webpack.config.js 파일을 만들어줍니다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Container</title>
  </head>
  <body>
    <h1>Container</h1>
    <div id = "micro-app-1"></div>
    <div id = "micro-app-2"></div>
  </body>
</html>

index.html 파일은 이렇게 구성합니다.

div 태그의 id는 앞서 작성한 microapp 1과 2에 있던 id와 동일해야 합니다.

import 'microapp1/App1';
import 'microapp2/App2';

index.js 파일은 이렇게 코딩해 줍니다.

 

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
    mode: 'development',
    devServer: {
        port: 8080,
    },
    plugins: [
        new ModuleFederationPlugin({
            name: 'container',
            remotes: {
                microapp1: 'microapp1@http://localhost:8081/remoteEntry.js',
                microapp2: 'microapp2@http://localhost:8082/remoteEntry.js',
            },
        }),
        new HtmlWebpackPlugin({
            template: './public/index.html',
        }),
    ],
};

다음으로 webpack.config.js 파일입니다.

포트는 8080이고 ModuleFederationPlugin을 가져온 것을 볼 수 있습니다.

plugin에도 Module Federation Plugin을 추가했고 container라는 이름을 달고 microapp1과 2를 추가했죠.

잘 보시면 아시겠지만 microapp1은 8081 포트의 remoteEntry.js에서 가져온다는 것을 볼 수 있습니다.

그리고 이 microapp1과 microapp2라는 이름은 앞서 index.js 파일에 사용했던 명칭이죠.

사실 순서상 webpack.config.js를 먼저 작성하는 게 맞긴 합니다만 크게 문제 될 건 없으니 진행했습니다.

 

그리고 사실상 이것보다 먼저 진행해 줘야 될 부분이 하나 있는데요.

일단 이대로 "npm run start"를 실행해 봅시다.

에러가 나죠.

이유는 microapp1과 2에 Module Federation Plugin을 설정해주지 않았기 때문이죠.

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
    mode: 'development',
    devServer: {
        port: 8081,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html',
        }),
        new ModuleFederationPlugin({
            name: 'microapp1',
            filename: 'remoteEntry.js',
            exposes: {
                './App1': './src/index',
            },
        }),
    ],
};

microapp1의 webpack.config.js 파일을 열어서 Module Federation Plugin을 불러오고 생성자를 이용해 플러그인에 추가해 줍니다.

microapp2 역시 name이 microapp2라는 것 말고는 다른 점이 없으니 따로 소스는 작성하지 않겠습니다.

 

이제 container의 index.js 파일의 이름을 bootstrap.js로 바꿔주고 index.js 파일을 새로 하나 만들어줍니다.

import ('./bootstrap')

 

index.js 파일의 소스코드는 이게 다입니다.

이 부분은 솔직히 저도 왜 이렇게 하는지 잘 모르겠는데 일단 이렇게 하라길래 해봤습니다.

 

여기까지 세팅이 되었다면 다시 container를 실행해 봅시다.

Container 앱을 실행한 화면
Container 앱을 실행한 화면

완벽하게 Container앱에서 microapp1과 2의 javascript 소스를 실행시킨 것을 확인할 수 있습니다.

F12를 눌러 개발자 도구에서 네트워크를 보면 localhost 8081과 8082에서 각각 remoteEntry.js 파일을 당겨온 것을 볼 수 있죠.

일단 여기까지 어떻게 webpack을 이용해서 마이크로 프론트엔드 서비스를 구현해 봤는데요.

제가 원하는 건 DB를 통한 메뉴 추가 삭제로 마이크로 프론트엔드 앱을 추가하고 빼는 건데 이런 방식이라면 쉽지는 않을 것 같네요.

다른 방식을 찾아봐야겠지만 마이크로 프론트엔드가 어떤 형태로 동작하는 건지는 어느 정도 이해가 된 것 같습니다.

 

참고자료

 

Comments