블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

06-25 03:49

Category

Recent Comment

'xplat'에 해당되는 글 1

  1. 2020.05.18 [Micro Frontend] Web Components 기반 마이크로 프론앤드
2020. 5. 18. 20:23 Angular/Architecture

Angular v6부터 Web Components에 대한 지원으로 @angular/elements 기능이 추가되어 Custom HTML Tag을 만들 수 있도록 지원한다. 본 글은 해당 사이트의 글Nx.dev 환경과 통합하여 개발하는 과정을 설명한다. Nx 환경은 mono repository 기반으로 multi application을 개발 할 수 있는 환경을 제공한다. Angular/CLI기반이지만 Angular, React, Node.js 개발까지 하나의 Git Repository안에서 개발하고 번들링 할 수 있도록 지원한다. 따라서 micro frontend에서 multi application 개발 잇점을 갖는다. 

 

블로그 소스 [GitHub]

 

 

NX 환경 준비

Angular v9.* 

@angular/cli v9.1.6

RxJS v6.5.*

Typescript v3.8.*

NodeJS v12.16.*

Node Version Manager(nvm)를 통해 로컬환경에 여러 Node버전을 관리하자.

// NodeJS
$ nvm install 12.16.2
$ nvm alias default 12.16.2
$ nvm use 12.16.2

// Angular/CLI 최신버전 사용
$ npm i -g @angular/cli@latest
$ npm i -g @nrwl/cli@latest
$ npm i -g yarn@latest

// local 설치
$ yarn add

NX workspace를 생성한다. 

$ npx create-nx-workspace@latest
// 선택 및 입력
? Workspace name (e.g., org name)     micro-demo
? What to create in the new workspace angular [a workspace with a single Angular application]
? Application name                    app-container
? Default stylesheet format           SASS(.scss)  [ http://sass-lang.com   ]

 

 

Web Components 개발 환경 설정

@angular/elements 를 설치한다. 

$ yarn add @angular/elements

UI Component로 ng-antd v9.1.* 를 사용한다. yarn 이 아니라 angular/cli의 'ng' 명령을 사용한다. Yes와 sidemenu 형태 선택한다.

$ ng add ng-zorro-antd

선택하기 
? Enable icon dynamic loading [ Detail: https://ng.ant.design/components/icon/en ] Yes
? Set up custom theme file [ Detail: https://ng.ant.design/docs/customize-theme/en ] Yes
? Choose your locale code: en_US
? Choose template to create project: sidemenu

설치 후에 Nx workspace와 ng-zorro-antd의 불일치를 해결한다.

  • apps/app-container/theme.less의 첫줄의 import 문구 수정
    • @import "../../../node_modules/ng-zorro-antd/ng-zorro-antd.less";
  • apps/app-container/index.html의 root tag 수정
    • <app-root></app-root>

설정 수정후 실행을 하면 sidemenu가 있는 환경이 자동 셋업되어 아래와 같이 보인다. 자세한 설치방법은 사이트를 참조한다.

$ ng serve --open

ng-zorro-antd 자동 적용 화면

 

 

 

Monitor Web Components 개발 및 번들링

 

monitor 애플리케이션을 신규 생성한다. 모니터 애플리케이션을 Web Components로 만들어 app-container 애플리케이션에서 동적으로 로딩해 본다. 

$ ng g app monitor

선택
? Which stylesheet format would you like to use? SASS(.scss)  [ http://sass-lang.com   ]
? Would you like to configure routing for this application? No

apps/monitor/src/app/app.component.html과 app.component.ts 를 변경한다. 

// app.component.html
{{title}} Application

// app.component.ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'micro-demo-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  @Input() title = 'monitor';
}

apps/monitor/src/app/app.module.ts에서 Web Comopennts를 등록한다. 

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [],
  // 동적으로 생성하므로 entryComponents에 등록
  entryComponents: [AppComponent],
  // 정적 bootstrap을 사용하지 않음
  // bootstrap: [AppComponent],
})
export class AppModule {
  constructor(private injector: Injector) {}

  ngDoBootstrap() {
    // createCustomElement를 통해 Web Components 스펙에 맞는 객체로 반환
    const monitorApp = createCustomElement(AppComponent, { injector: this.injector });
    // browser window객체에 잇는 customElements를 통해 Web Components 등록
    customElements.define('monitor-app', monitorApp);
    // 사용방법: @Input() title이 있으므로 attribute 설정가능
    // <monitor-app title="Monitor Application"></monitor-app>
  }
}

monitor 애플리케이션을 번들링하면 여러개의 파일로 나오는데 번들링 파일을 최소화한다. ngDoBootstrap() 은 정적 bootstrap이 아닌 실행타임에 외부 컴포넌트를 동적으로 로딩할 때 애플케이션 root를 결정할 수 있게 한다. ngDoBootstrap에 대한 설명을 참조하자.

$ ng build monitor --prod --output-hashing=none

수행할 경우 main, polyfill, runtime등의 파일이 생성된다. 파일 최소화를 위해 ngx-build-plus 패키지를 이용한다. 

monitor 애플리케이션의 번들링된 파일들

ng add 명령으로 ngx-build-plus를 설치하고 애플리케이션은 monitor를 지정한다. 

$ ng add ngx-build-plus --project=monitor

ng add 로 수행을 하면 angular.json 파일의 설정을 자동으로 적용해 준다. builder의 명령어를 자동 수정함.

    "monitor": {
      "projectType": "application",
      "schematics": {
        "@nrwl/angular:component": {
          "style": "scss"
        }
      },
      "root": "apps/monitor",
      "sourceRoot": "apps/monitor/src",
      "prefix": "micro-demo",
      "architect": {
        "build": {
          "builder": "ngx-build-plus:browser",  <== builder가 자동 변경됨
          "options": {
          ...
          

다시 명령으로 monitor 애플리케이션을 번들링한다. ngx-build-plus로 확장한 옵션인 --single-bundle true를 추가한다.

$ ng build monitor --prod --output-hashing=none --single-bundle true

main, polyfill로 압축된 번들링 파일

main과 polyfill을 합쳐주는 스크립트를 등록한다. Mac/Linux기준 명령이다. micro-demo 폴더 밑에 buildSingle.sh 파일을 생성한다. 번들링파일 합치는 명령을 넣는다.  

 

#!/bin/sh
ng build monitor --prod --output-hashing=none --single-bundle true && cat dist/apps/monitor/main-es5.js dist/apps/monitor/polyfills-es5.js > apps/app-container/src/assets/monitor-es5.js

 

buildSingle.sh를 수행한 결과 파일은 app-container의 apps/app-container/src/assets/monitor-es5.js 쪽으로 copy된다.

 

 

 

컨테이너 애플리케이션에서 동적로딩

Web Components 스펙을 기준으로 monitor 애플리케이션을 번들링 했기때문에 프레임워크의 종류에 상관없이 <monitor-app> 태그를 사용할 수 있다. 정적으로 사용하는 방법을 살펴보자.

 

apps/app-container/src/app/pages/welcome/welcome.component.ts에서 monitor-es5.js파일을 import한다. 

import { Component, OnInit } from '@angular/core';
// 소스 import
import '../../../assets/monitor-es5.js';  

@Component({
  selector: 'app-welcome',
  templateUrl: './welcome.component.html',
  styleUrls: ['./welcome.component.scss']
})
export class WelcomeComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

 

테스트를 위해 welcome.component.html 에 <monitor-app>태그를 설정해 보자. 

<monitor-app title="Hi Monitor"></monitor-app>

 

여기까지하고 수행을 하면 <monitor-app> 태그를 해석할 수 없다고 Angular가 에러를 뱃는다. <monitor-app> 은 Angular가 해석하는 것이 아니라 Browser에서 해석되는 Web Components이므로 무시하도록 welcome.module.ts에 CUSTOM_ELEMENTS_SCHEMA를 설정한다. 

 

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { WelcomeRoutingModule } from './welcome-routing.module';
import { WelcomeComponent } from './welcome.component';

@NgModule({
  imports: [WelcomeRoutingModule],
  declarations: [WelcomeComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  exports: [WelcomeComponent]
})
export class WelcomeModule { }

 

설정한 "Hi Monitor"와 함께 <monitor-app>이 출력
브라우져가 해석한 <monitor-app> 태그

 

<monitor-app>태그를 설정하지 않고 Javascript를 이용하여 로딩해 본다. 

apps/app-container/src/app/monitor.service.ts 파일을 생성한다. 

  • monitor-es5.js 파일 동적 로딩
  • <monitor-app> DOM 동적 추가
import { Injectable } from '@angular/core';

@Injectable({providedIn: 'root'})
export class MonitorLoaderService {
  loaded = false;

  constructor() { }

  // script 동적 로딩
  loadMonitorScript(): void {
    if (this.loaded) {
      return;
    }

    const script = document.createElement('script');
    script.src = 'assets/monitor-es5.js';
    document.body.appendChild(script);
    this.loaded = true;
  }

  // <monitor-app> 태그 추가
  addMonitorApp(): void {
    const tile = document.createElement('monitor-app');
    // @Input() 내용은 setAttribute로 추가 가능
    tile.setAttribute('title', 'Dynamic Load Monitor');

    const content = document.getElementById('content');
    content.appendChild(tile);
  }
}

 

 위의 경우 monitor-es5.js 파일을 별도로 다운로드받아 동적 로딩을 수행한다. 

monitor-es5.js 파일을 <script> 태그 추가후 동적으로 다운로드 받음

 

 

공통 파일 빼고 번들링하기

만일 app-container과 monitor 에서 사용하는 공통 패키지의 버전이 같다면, app-container 애플리케이션과 monitor 애플리케이션이 공통으로 사용하는 파일중, monitor 애플리케이션을 번들링할 때 공통파일을 제거하는 방법에 대해 알아보자. 제거를 통해 monitor 애플리케이션의 번들링 사이즈를 줄일 수 있다. 이는 Network payload time을 줄여주는 결과를 갖는다. 

 

ngx-build-plus를 이용해서 @angular/cli의 webpack externals 을 자동 생성한다. (참조)

$ ng g ngx-build-plus:externals --project monitor

수행을 하면 angular.json 파일에 별도 환경이 추가되고, apps/monitor/webpack.externals.js 파일이 생성된다. angular.json 내용중

"node_modules/@angular/elements/bundles/elements.umd.js", 내용은 제거한다. elements.umd.js를 공통파일로 빼서 사용하는데 오류가 있다. 

elements.umd.js 를 포함시킬 경우 오류가 발생함

angular.json의 monitor 애플리케이션으 "scripts" 설정 내역 => scripts.js 파일에 설정한 *.umd.js 파일을 합친다.

// angular.json 내의 monitor 애플리케이션 설정
// "scripts"에 externals로 참조하는 파일 설정이 자동으로 입력되어 진다.
    "monitor": {
      "projectType": "application",
      "schematics": {
        "@nrwl/angular:component": {
          "style": "scss"
        }
      },
      "root": "apps/monitor",
      "sourceRoot": "apps/monitor/src",
      "prefix": "micro-demo",
      "architect": {
        "build": {
          "builder": "ngx-build-plus:browser",
          "options": {
            "outputPath": "dist/apps/monitor",
            "index": "apps/monitor/src/index.html",
            "main": "apps/monitor/src/main.ts",
            "polyfills": "apps/monitor/src/polyfills.ts",
            "tsConfig": "apps/monitor/tsconfig.app.json",
            "aot": true,
            "assets": [
              "apps/monitor/src/favicon.ico",
              "apps/monitor/src/assets"
            ],
            "styles": [
              "apps/monitor/src/styles.scss"
            ],
            "scripts": [
              "node_modules/rxjs/bundles/rxjs.umd.js",
              "node_modules/@angular/core/bundles/core.umd.js",
              "node_modules/@angular/common/bundles/common.umd.js",
              "node_modules/@angular/common/bundles/common-http.umd.js",
              "node_modules/@angular/compiler/bundles/compiler.umd.js",
              "node_modules/@angular/elements/bundles/elements.umd.js", // <-- 제거한다
              "node_modules/@angular/platform-browser/bundles/platform-browser.umd.js",
              "node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"
            ]
          },

apps/monitor/webpack.externals.js  파일내역에서 ng.elements도 주석처리한다.  => monitor-es5.js 파일에서 제거되는 파일목록이다.

const webpack = require('webpack');

module.exports = {
    "externals": {
        "rxjs": "rxjs",
        "@angular/core": "ng.core",
        "@angular/common": "ng.common",
        "@angular/common/http": "ng.common.http",
        "@angular/platform-browser": "ng.platformBrowser",
        "@angular/platform-browser-dynamic": "ng.platformBrowserDynamic",
        "@angular/compiler": "ng.compiler",
        // "@angular/elements": "ng.elements",  <-- 주석처리한다.

        // Uncomment and add to scripts in angular.json if needed
        // "@angular/router": "ng.router",
        // "@angular/forms": "ng.forms"
    }
}

buildSingle.sh 내용을 수정한다. scripts.js 파일은 window.ng.core 또는 window.ng.common과 같은 global 객체가 담겨있는 파일이다. 따라서 scripts.js는 app-container 애플리케이션에서 최초 한번만 로딩하면 되고, 이후 monitor 애플리케이션과 같은 web components는 번들 파일은 자신의 내용만을 포함한다.

 

monitor app size including common library  - 129KB

common library를 포함한 사이즈 - 129KB

monitor app size excluding common library - 19KB

monitor app과 @angular/elements만 포함한 사이즈 - 19KB

#!/bin/sh
ng build monitor --prod --extra-webpack-config=apps/monitor/webpack.externals.js --output-hashing=none --single-bundle true && cat dist/apps/monitor/main-es5.js dist/apps/monitor/polyfill-es5.js > apps/app-container/src/assets/monitor-es5.js
cat dist/apps/monitor/scripts.js > apps/app-container/src/assets/scripts.js

scripts.js 파일은 angular.json 의 app-container 애플리케이션 "scripts" 옵션에 추가한다. 

    "app-container": {
      "projectType": "application",
      "schematics": {
        "@nrwl/angular:component": {
          "style": "scss"
        }
      },
      "root": "apps/app-container",
      "sourceRoot": "apps/app-container/src",
      "prefix": "micro-demo",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/apps/app-container",
            "index": "apps/app-container/src/index.html",
            "main": "apps/app-container/src/main.ts",
            "polyfills": "apps/app-container/src/polyfills.ts",
            "tsConfig": "apps/app-container/tsconfig.app.json",
            "aot": true,
            "assets": [
              "apps/app-container/src/favicon.ico",
              "apps/app-container/src/assets",
              {
                "glob": "**/*",
                "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
                "output": "/assets/"
              }
            ],
            "styles": [
              "apps/app-container/src/theme.less",
              "apps/app-container/src/styles.scss"
            ],
            "scripts": [
              "apps/app-container/src/assets/scripts.js"  <== 추가
            ]
          },

수행하면 네트워크에서 scripts.js 파일은 한번만 로딩되고, 이후 다양한 Web Components들은 자신의 애플리케이션 내역만 포함한 파일로 번들링하여 파일 사이즈를 줄일 수 있다. (참조)

Custom Element에서 common library를 window객체쪽으로 변경함.

 

scripts.js 로딩 후 monitor-es5.js 파일 로딩

 

 

참조

https://www.angulararchitects.io/aktuelles/angular-elements-part-i/

 

Angular Elements, Part I - ANGULARarchitects

A dynamic dashboard in four steps with Web Components

www.angulararchitects.io

https://ng.ant.design/docs/getting-started/en

 

NG-ZORRO - Ant Design Of Angular

An enterprise-class UI design language and Angular-based implementation with a set of high-quality Angular components, one of best Angular UI library for enterprises

ng.ant.design

https://medium.com/angular-in-depth/how-to-manually-bootstrap-an-angular-application-9a36ccf86429

 

How to manually bootstrap an Angular application

AngularInDepth is moving away from Medium. This article, its updates and more recent articles are hosted on the new platform inDepth.dev

medium.com

https://www.angulararchitects.io/aktuelles/your-options-for-building-angular-elements/

 

Your options for building Angular Elements - ANGULARarchitects

with the CLI

www.angulararchitects.io

https://indepth.dev/tiny-angular-application-projects-in-nx-workspaces/

 

Tiny Angular application projects in Nx workspaces

Use assets, styles, and environments workspace libraries to follow the Single Responsibility Principle. Step-by-step commands and instructions.

indepth.dev

https://nstudio.io/blog/custom-web-elements-with-angular-and-react

 

nstudio | Custom web elements for Angular and React with Nx + xplat

Passionate about implementing creative solutions for you. Technology, Consulting, Audio/Video Production. Web/Mobile apps, Angular/NativeScript plugins, Vue/NativeScript, product development, team training and help with project features/objectives.

nstudio.io

 

posted by peter yun 윤영식
prev 1 next