시나리오
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를 볼 수 있게 된다.