무작정 MFA 체험하기 - Module Federation
최근 회사에서 MFA를 활용한 프로젝트를 진행해 보자는 이야기가 있어 공부를 하고 있습니다.
해당 개념을 알게 된 지는 얼마 되지 않았는데, 우선 MFA를 구현하는 방법 중 하나인 Module Federation을 간단히 체험해 보았습니다.
MFA란?
MFA (Micro Frontend Architecture)는 프론트엔드 서비스를 작은 단위로 쪼개어 개별적으로 개발◦배포를 진행하고, 이를 조합하여 통합 서비스를 만드는 방법론입니다. 개별 코드와 리소스를 기능별로 분리하기 때문에 유지보수와 재사용이 용이하고, 서로 독립적인 작업이 가능하기 때문에 조직 운영에도 도움이 되는 등 대규모 서비스 또는 조직에서 강점을 보이는 방식이라고 할 수 있습니다.
MFA를 구현하는 방법에는 여러 가지가 있습니다. 패키지로 만들어 빌드 과정에서 통합을 하는 방법도 있고, <iframe>
태그를 사용하는 방법도 있습니다. 하지만 저희 측에서는 JavaScript 번들을 런타임에서 로드해서 합치는 방식인 Module Federation을 고려했고 이쪽으로 테스트를 진행하기로 했습니다.
Module Federation 적용해 보기
예시를 위해 CRA로 간단하게 2개의 앱을 구성했습니다. 목표는 첫 화면(이하 외부)의 카드를 호스트로 불러오는 것입니다.
외부 카드는 아래와 같은 데이터를 받아 teamName
, teamImgUrl
정보를 사용하고 있습니다.
호스트 카드는 편의상 하드코딩되어 있습니다.
{
"teamName": "T1",
"teamImgUrl": "<t1-url>",
"playerName": "Faker",
"playerImgUrl": "<faker-url>"
}
Module Federation을 사용하기 위해서는 webpack을 설정해야 합니다.
초기화에는 npx webpack init
명령어를 사용했는데, 직접 설치를 해도 됩니다.
여러 설정 중에서도 가장 중요한 부분은 webpack.config.js
파일 설정이었습니다.
const { ModuleFederationPlugin } = require("webpack").container;
const { FederatedTypesPlugin } = require("@module-federation/typescript");
// ...
const federationConfig = {
name: "exportapp",
filename: "remoteEntry.js",
exposes: {
"./Team": "./src/component/Team.tsx",
"./types": "./src/@types/index.ts",
},
shared: {
...deps,
react: { singleton: true, eager: true, requiredVersion: deps.react },
"react-dom": {
singleton: true,
eager: true,
requiredVersion: deps["react-dom"],
},
// ...
},
};
// ...
const config = {
entry: "./src/index.ts",
// ...
plugins: [
// ...
new ModuleFederationPlugin(federationConfig),
new FederatedTypesPlugin({ federationConfig }),
],
// ...
};
// ...
위는 외부 모듈의 설정 파일입니다. federationConfig
변수에 Module Federation 설정이 들어 있는데, 주목할 부분은 다음과 같습니다.
name
은 전체 모듈의 이름으로, 다른 앱과 중복되지 않아야 합니다.filename
은 내보낼 파일의 이름입니다.exposes
는 어떤 소스를 어떤 이름으로 내보낼지 결정합니다.
name
과 함께 모듈 호출에 사용되기 때문에 정확히 설정해 주어야 합니다.
Module Federation에 필수적인 ModuleFederationPlugin
외에, TypeScript에서 타입 공유를 위해 추가로 FederatedTypesPlugin
을 사용하였습니다. 우아한형제들에서는 양방향 타입 공유를 위해 @module-federation/native-federation-typescript 플러그인을 사용했는데, 저의 경우 아직 양방향 타입 공유를 고려할 단계는 아니라고 판단했습니다.
// ...
const federationConfig = {
name: "hostapp",
remotes: {
sample1: "exportapp@http://localhost:3001/remoteEntry.js",
},
shared: {
// ...
},
};
// ...
호스트의 설정 파일도 거의 동일하지만, 모듈을 불러오기 위해 remotes
옵션을 설정했습니다. 외부 모듈에서 설정한 이름과 파일명, 그리고 접근 주소를 사용해 값을 입력합니다.
이렇게 설정을 마치고 서비스들을 재실행하면 호스트에 타입 파일이 생성됩니다.
생성된 타입 파일을 이용하여 호스트에서 코드를 작성할 수 있습니다. VS Code 기준 코드 작성 시에도 타입을 올바르게 불러오는 것을 확인할 수 있었습니다.