728x90
MicroFrontend를 도입하고 싶다고 생각하게 된 계기는 다음과 같다.
- 큰 서비스 안에 내가 만들고 있는 서비스가 있다
- 빌드와 배포가 오래걸린다
- lint, prettier 등을 고려하지 않은 팀도 있기에 불편하게 생각할 수 있으니 우리팀만 설득해서 하고 싶다
- 부분장애가 모든서비스로 전파되지않음
- 자유로운 기술 스택
- 코드 복잡성 제거
- 신규 서비스 추가의 자유로움
시도
- Single SPA
- 여러가지 app을 따로 실행시켜 한 화면에 렌더링 O
- 원하는 프로젝트를 소스로 가능 (vue, react, ts, vite, …)
- html,js 혹은 ejs 위에 여러개 얹는식
{
"imports": {
"navbar": "http://localhost:8080/js/app.js",
"vms": "http://localhost:8081/js/app.js",
"app2": "http://localhost:8082/js/app.js",
"single-spa": "https://cdnjs.cloudflare.com/ajax/libs/single-spa/4.3.7/system/single-spa.min.js",
"vue": "https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js",
"vue-router": "https://cdn.jsdelivr.net/npm/vue-router@3.0.7/dist/vue-router.min.js"
}
}
<script>
(function() {
Promise.all([System.import('single-spa'), System.import('vue'), System.import('vue-router')]).then(function (modules) {
var singleSpa = modules[0];
var Vue = modules[1];
var VueRouter = modules[2];
Vue.use(VueRouter)
singleSpa.registerApplication(
'navbar',
() => System.import('navbar'),
location => true
);
singleSpa.registerApplication(
'vms',
() => System.import('vms'),
location => location.pathname.startsWith('/vms')
)
singleSpa.registerApplication(
'app2',
() => System.import('app2'),
location => location.pathname.startsWith('/app2')
)
singleSpa.start();
})
})()
</script>
<!-- See https://github.com/joeldenning/import-map-overrides#user-interface -->
<import-map-overrides-full show-when-local-storage="overrides-ui"></import-map-overrides-full>
- 각각 서비스 키고 root 서비스에서 확인하는 식
- public-path 설정했지만 vms에서 /assets /translate 이미 설정되어 있는거 바꾸는법을 조사해야함 (i18n이 아니기 때문)
- 전체 라우트 관리 하는법, layout(login, logout), build
- 활발하지 않은 github commit 흔적들
- 유튜브에 3년전~ 요즘에도 간간히 나온다
예제 코드를 통해 만들어 봤는데 이건 아니다는 느낌이었다. 다른 방법은 없나 다시 찾아봤다.
- Webpack module federation
- 여러가지 app 한 화면에 렌더링 O
- 원하는 프로젝트를 소스로 가능 (vue, react, ts, vite, …)
- 유튜브 작년~
- module-federation/module-federation-examples
- Domain별, Design Component(Widget) 별
webpack, @vue/cli module, vuetify 버전 update, bootstrap 말고는 따로 할게 없었다
Route, Vuex는 마이크로 서비스에서 등록하더라도 shell app에서도 따로 등록 필요
import localRoutes from './routes';
import facebookRoutes from 'facebook/routes';
const routes = [...localRoutes, ...remoteRoutes];
확인 안해봄
- hot-reload
- different version (vue2+vue3)
- adapter 필요
- 이 부분이 내 생각과 달리 허들이 될거 같았다. 자유로운 기술스택을 쉽게 사용할줄 알았지만 React + Vue2 + Vue Cli2 + Vue3 + Angular 로 하기엔 한 곳으로 정리해야했고 Adapter가 필요했다.
# 이건 vue2 -> vue3
export function vue2ToVue3(WrapperComponent, wrapperId) {
let vm;
return {
mounted() {
const slots = bindSlotContext(this.$slots, this.__self);
vm = new Vue2({
render: createElement => {
return createElement(
WrapperComponent,
{
on: this.$attrs,
attrs: this.$attrs,
props: this.$props,
scopedSlots: this.$scopedSlots,
},
slots,
);
},
});
vm.$mount(`#${wrapperId}`);
},
props: WrapperComponent.props,
render() {
vm && vm.$forceUpdate();
},
};
}
# 이건 react different version
class Adapter extends React.Component {
constructor(props) {
super(props);
this.refHold;
}
init = hydrate => {
(async () => {
const ReactDOM = (await import('app2/newReactDOM')).default;
const React = (await import('app2/newReact')).default;
const RemoteComponent = await this.props.importer();
const { importer, children, ...rest } = this.props;
const renderMethod = hydrate ? ReactDOM.hydrate : ReactDOM.render;
renderMethod(React.createElement(RemoteComponent.default, rest, children), this.refHold);
})();
};
componentDidUpdate(prevProps, prevState, snapshot) {
this.init(true);
}
componentDidMount() {
this.init();
}
render() {
return (
<div
style={{ border: '1px red solid', padding: '10px', margin: '20px 0' }}
ref={ref => (this.refHold = ref)}
/>
);
}
}
export default Adapter;
우선 우리 서비스도 같은 버전을 이용한다면 큰 문제 없이 원하는 이점은 취할 수 있어보인다.
'software engineering > frontend' 카테고리의 다른 글
React State Management (0) | 2022.08.25 |
---|---|
Unit Test (0) | 2022.08.25 |
Framer with React (0) | 2022.08.25 |
Motion UI (feat. Framer Motion) (0) | 2022.08.25 |
국제화 (i18n) 자동화 (0) | 2022.08.25 |