지난번에 이어서 마이크로 프론트엔드를 좀 더 파헤쳐 보자.
저번에 구현 부분을 네트워크탭에서 확인해보면 cart와 products에서 동시에 faker 모듈을 분리해서 불러오고 있는것을 볼 수 있다. 하지만 faker은 중첩된 모듈이고 번들 사이즈도 매우 큰편이기에 효율적이지 않다. 이 중첩된 모듈을 하나로 공유할 수 있도록 만들어보자.
방법은 굉장히 단순하다 module federation에서 제공하는 shared 옵션을 사용하면 된다. 하지만 이 옵션을 사용하기전에 product와 cart를 비동기적으로 불러올 수 있게하는 작업을 해줘야한다.
import("./bootstrap");
import faker from "faker";
const cartText = `<div>You have ${faker.random.number()}</div>`;
document.querySelector("#cart-dev").innerHTML = cartText;
위와 같이 bootstrap.js를 생성해서 index.js에서 import() 구문을 사용해서 불러와주도록 하자. import를 ()처럼 함수형으로 불러오게 되면 페이지가 로드 되기전에 bootstrap 파일을 비동기적으로 먼저 준비할 수 있도록 할 수 있다. 위와 같은 세팅은 product에도 똑같이 적용시키자.
그 다음 prdouct와 cart에 아래와 같이 shared 옵션을 추가해주자.
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
devServer: {
port: 8082,
},
plugins: [
new ModuleFederationPlugin({
name: "cart",
filename: "remoteEntry.js",
exposes: {
"./CartShow": "./src/index",
},
// faker 모듈을 다른곳에서 이미 로드했다면 중첩이 안되도록 도와주는 기능이다.
shared: ["faker"],
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
],
};
이제 결과물을 네트워크 텝을 확인해보면 faker 모듈을 한번만 불러오는것을 확인할 수 있다.
현재는 container에서 다른 프로젝트를 불러주기 위해선 product나 cart에서 지정해준 id값을 container index.html에도 똑같은 id값을 지정해줘야지만 렌더링 되는것을 볼 수 있다. 그러기 위해선 다른 프로젝트의 개발환경도 다 고려하고 있어야해서 자율도가 떨어진다. 이를 해결하기 위해 프로젝트 구성들을 좀 바꿔보도록하자.
현재 환경이 local에서 개발되고 있는지 container 환경에서 개발되고 있는지를 판단해서 그 환경에 맞는 index.html를 가져와서 원하는 view를 그려주도록 한다.
import faker from "faker";
const mount = (el) => {
let products = "";
for (let i = 0; i < 3; i++) {
const name = faker.commerce.productName();
products += `<div>${name}</div>`;
}
el.innerHTML = products;
};
// Context/Situation #1
// We are running this file in develpment in isolation
// We are using our local index.html file
// Which DEFNITELY has an element with an id of 'dev-product'
// We want to immediately render our app into that element
if (process.env.NODE_ENV === "development") {
const el = document.querySelector("#dev-products");
// Assuming our container doesnt have an element
// with id 'dev-products'...
if (el) {
// We are probably runnning in isolation
mount(el);
}
}
// Context/Situation #2
// We are running this file in develpment or production
// through the CONTAINER app
// NO GUARANTEE that an element with an id of 'dev-products'
// WE DO NOT WANT try to immediately render the app
export { mount };
<!DOCTYPE html>
<html>
<head></head>
<body>
<div id="my-products"></div>
<div id="my-cart"></div>
</body>
</html>
import { mount as productsMount } from "products/ProductsIndex";
import { mount as cartMount } from "cart/CartShow";
console.log("Container!");
productsMount(document.querySelector("#my-products"));
cartMount(document.querySelector("#my-cart"));
이렇게 구현함으로써 container에서 유동적으로 내가 원하는 위치에 원하는 id를 지정해서 다른 프로젝트를 보여줄 수 있게 되었다.