마이크로 프론트엔드 - Web Component를 활용한 Fragment 통합

시나리오

1. Team Home이 운영하는 정적 파일을 제공하는 웹 서버 (localhost:3001)

    - Web Component: /bundle.js

2. Team Jobs이 운영하는 정적 파일을 제공하는 웹 서버 (localhost:3002)

    - Web Component: /bundle.js

3. Web Service를 제공하는 Shell App (localhost:3000)

    - page: /index.html

 

 

루트 프로젝트 초기 세팅

다음 명령어를 순차적으로 실행해서 프로젝트를 구축한다.

pnpm init
corepack use pnpm@8.10.0
pnpm add turbo -D

 

pnpm-workspace.yaml을 통해 워크스페이스 설정을 추가한다.

packages:
  - "teams/*"
  - "apps/*"

 

 

모든 서버를 동시에 실행하기 위해 turbo.json도 추가해주도록 하자

{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

Shell 서버 만들기

루트 디렉토리에 apps/shell 디렉토리를 만들고 프로젝트를 초기화해주고, serve 패키지를 설치해준다.

pnpm init
pnpm --filter shell add serve

 

그 다음 package.json에 dev 명령어를 추가해준다.

{
  "name": "shell",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "serve -s public -p 3000",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "serve": "^14.2.4"
  }
}

 

각 팀의 워크스페이스 구축하기

teams/team-home과 teams/team-jobs 디렉토리를 각각 생성해주고, 각 워크스페이스를 초기화해주도록 하자. 

그리고 각 워크스페이스에도 serve 패키지를 설치해주도록 하자

pnpm init 
pnpm --filter team-home add serve
pnpm --filter team-jobs add serve

 

team-home, team-jobs에 각각 dev 명령어를 추가해주도록 하자

# teams/team-home
{
  "name": "team-home",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "serve public -p 3001 --cors",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "serve": "^14.2.4"
  }
}

# teams/team-jobs
{
  "name": "team-jobs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "serve public -p 3002 --cors",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "serve": "^14.2.4"
  }
}

 

여기까지 완료하면 다음 명령어를 통해 shell, team-home, team-jobs 서버를 모두 띄울 수 있게 된다.

pnpm exec turbo dev

 

각 팀 별 Web Component 만들기

team-home에 public 폴더를 만들고 bundle.js를 생성하여 다음과 같은 코드를 추가하도록 하자.

class HomeComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });

    const template = document.createElement("template");

    template.innerHTML = `
      <style>
        h2 {
          color: red;
        }
      </style>
      <h2>홈</h2>
      <button id="btn">버튼</button>
    `;

    this.shadowRoot.appendChild(template.content.cloneNode(true));

    this.shadowRoot
      .querySelector("#btn")
      .addEventListener("click", this.onClick);
  }

  onClick() {
    alert("버튼 클릭");
  }
}

customElements.define("home-component", HomeComponent);

 

이번에는 team-jobs에 public 폴더를 만들고 bundle.js를 생성하여 다음과 같은 코드를 추가하자.

class JobsComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });

    const template = document.createElement("template");
    template.innerHTML = `
      <style>
        h2 {
          color: green;
        }
      </style>
      <h2>채용공고</h2>
    `;

    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
}

customElements.define("jobs-component", JobsComponent);

 

Shell 서버에서 Web Component 사용하기

마지막으로 apps/shell에서 web-component를 사용하는 코드를 작성하자.

public 폴더에 index.html을 추가해준다.

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

    <script src="http://localhost:3001/bundle.js"></script>
    <script src="http://localhost:3002/bundle.js"></script>
  </head>
  <body>
    <h1>애플리케이션 루트</h1>
    <nav>
      <ul>
        <li><a href="/">홈</a></li>
        <li><a href="/jobs">채용공고</a></li>
      </ul>
    </nav>
    <div id="app"></div>
    <script type="text/javascript">
      const webComponentsByRoute = {
        "/": "home-component",
        "/jobs": "jobs-component",
      };
      const webComponentType = webComponentsByRoute[window.location.pathname];

      const root = document.getElementById("app");
      const webComponent = document.createElement(webComponentType);
      root.appendChild(webComponent);
    </script>
  </body>
</html>

 

이렇게 까지 구현하고 Shell 서버(localhost:3000)에 접속하게 된다면 다음과 같이 Web Component를 볼 수 있게 된다.