소스 폴더로 이동을 하면 지정한 node version으로 nvm을 통해 switching 하고 싶다면 폴더에 .nvmrc 파일을 생성하고 "14.18.0" 버전을 명시하고, 로그인 사용자 .zshrc 또는 .bashrc 에 하기 사항을 설정한다. Gist 소스 참조
#! /usr/bin/env zsh
# Ref: https://github.com/creationix/nvm#calling-nvm-use-automatically-in-a-directory-with-a-nvmrc-file
# place this after nvm initialization!
autoload -Uz add-zsh-hook
# Function: load-nvmrc
load-nvmrc() {
local _CUR_NODE_VER="$(nvm version)"
local _NVMRC_PATH="$(nvm_find_nvmrc)"
if [[ -n "${_NVMRC_PATH}" ]]; then
local _NVMRC_NODE_VER="$(nvm version "$(cat "${_NVMRC_PATH}")")"
if [[ "${_NVMRC_NODE_VER}" == 'N/A' ]]; then
local compcontext='yn:yes or no:(y n)'
vared -cp "Install the unmet version ($(cat "${_NVMRC_PATH}")) in nvm (y/n) ?" _ANSWER
if [[ "${_ANSWER}" =~ '^y(es)?$' ]] ; then
nvm install
fi
elif [[ "${_NVMRC_NODE_VER}" != "${_CUR_NODE_VER}" ]]; then
nvm use
fi
elif [[ "${_CUR_NODE_VER}" != "$(nvm version default)" ]]; then
echo -e "Reverting to the default version in nvm"
nvm use default
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc
NX 개발환경 구성을 위한 글로벌 패키지를 설치한다. Typescript는 v4.3.5 이상을 사용한다.
$> npm i -g @angular/cli@latest
$> npm i -g @nrwl/cli@latest
$> npm i -g yarn@latest
NX 개발환경 생성하기
npx 명령으로 개발환경 생성. RNN Stack에서 RNN은 React NestJS NextJS 을 합친 것이다. React Application의 명칭은 "tube-csr" 이다.
반드시 latest 버전으로 설치한다.
$> npx create-nx-workspace@latest
선택하기
✔ Workspace name (e.g., org name) · rnn-stack
✔ What to create in the new workspace · react
✔ Application name · tube-csr
✔ Default stylesheet format · scss
✔ Use Nx Cloud? (It's free and doesn't require registration.) · No
$> cd rnn-stack
"tube-csr" 애플리케이션을 위한 Node서버로 NestJS 플로그인를 설치하고, "tube-api" 이름으로 서버를 생성한다.
Nx의 NestJS 플러그인 설치
$> yarn add -D @nrwl/nest@latest
생성
$> nx generate @nrwl/nest:app tube-api
다음으로 NX의 NextJS 플러그인을 설치한다. NextJS Application은 "tube-ssr" 이다.
설치
$> yarn add -D @nrwl/next@latest
생성
$> nx generate @nrwl/next:app tube-ssr
또는
$> nx g @nrwl/next:app tube-ssr
✔ Which stylesheet format would you like to use? · scss
NextJS 애플리케이션을 설치하면 sass-node버전을 v5.0.0이 설치된다. v4.14.1로 변경 사용한다. 5.0으로 사용시 컴파일 오류 발생하여 향후 패치되면 버전 업그레이드 함.
tube-ssr 애플리케이션의 개발 서버 포트는 4300 으로 변경한다. workspace.json의 "tube-ssr"의 serve options설정에 포트 정보를 수정한다.
Nx 환경파일 재구성
애플리케이션과 라이브러리를 많이 만들다 보면 rnn-stack/workspace.json 파일안에 설정이 계속 추가된다. 가독성을 위하여 애플리케이션(라이브러리 포함) Nx의 환경설정을 프로젝트별 별도 파일로 분리한다. 분리후에는 애플리케이션이나 라이브러리 생성시 자동으로 "project.json" 파일로 분리가 된다.
각 애플리케이션 root 폴더에 "project.json" 파일을 생성하고 workspace.json의 프로젝트별 설정 정보를 옮긴다. workspace.json에는 애플리케이션의 위치정보로 수정하면 컨벤션에 의해 project.json을 인지한다.
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 개발 잇점을 갖는다.
// 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
설정 수정후 실행을 하면 sidemenu가 있는 환경이 자동 셋업되어 아래와 같이 보인다. 자세한 설치방법은 사이트를 참조한다.
$ ng serve --open
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 패키지를 이용한다.
ng add 명령으로 ngx-build-plus를 설치하고 애플리케이션은 monitor를 지정한다.
$ ng add ngx-build-plus --project=monitor
ng add 로 수행을 하면 angular.json 파일의 설정을 자동으로 적용해 준다. builder의 명령어를 자동 수정함.
테스트를 위해 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 { }
<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 파일을 별도로 다운로드받아 동적 로딩을 수행한다.
공통 파일 빼고 번들링하기
만일 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를 공통파일로 빼서 사용하는데 오류가 있다.
angular.json의 monitor 애플리케이션으 "scripts" 설정 내역 => scripts.js 파일에 설정한 *.umd.js 파일을 합친다.
buildSingle.sh 내용을 수정한다. scripts.js 파일은 window.ng.core 또는 window.ng.common과 같은 global 객체가 담겨있는 파일이다. 따라서 scripts.js는 app-container 애플리케이션에서 최초 한번만 로딩하면 되고, 이후 monitor 애플리케이션과 같은 web components는 번들 파일은 자신의 내용만을 포함한다.