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

Publication

Category

Recent Post

2019. 4. 8. 17:45 Angular/Architecture

플랫폼위에 필요에 따라 플러그인 파일을 동적으로 다운로드 받아 운영하는 방식에 대한 기술 검토를 한다. 하나는 Single Application이면서 필요시점에 파일을 다운로든 받는 방식과 두번째는 다른 애플리케이션의 파일을 다운로드 받아 처리하는 방식이다. 

 

첫번째는 Single Application Plugin 방식으로 하나의 애플리케이션이 서비스되면서 필요시점에 파일을 다운로드 받아서 사용하는 방법이다. 

  • platform, shared 파일을 최초에 다운도로드 받아 처리한다. 
  • 화면-1로 페이지가 전화될 때 plugin-1 파일을 다운로드 받아 처리한다.
  • 즉, 플랫폼과 플러그인 파일이 같은 서버에 존재하는 경우이다.

 

두번째는 Multi Application Plugin 방식으로 여러 애플케이션을 하나의 플랫폼에서 운영하는 방식이다. 

  • Remote Server-1/2/3은 각기 다른 애플리케이션이라 본다. 
  • Proxy Server는 플랫폼 서비스를 담당하고, Remote Server로의 요청을 중간에서 처리하는 Proxy 역할을 수행한다. 이때 Auth에 대한 권한을 처리할 수도 있다. 
  • 화면-1에 대한 요청을 Proxy Server에 하면 Url context 구분을 통해 Remote Server-1 서비스에 Plugin-1 파일을 요청 처리한다.
  • 즉, 플랫폼과 플러그인 파일이 존재하는 서버 위치가 서로 틀리다.

 

Single Application Plugin 방식 구성

@angular/cli와 @nrwl/schematics를 통해 애플케이션을 생성한다.  Node 버전은 LTS최신 버전을 사용하고 yarn도 설치한다.

$ node --version
v10.15.3
$ npm i -g yarn @angular/cli @nrwl/schematics @nestjs/cli
+ @angular/cli@7.3.8
+ @nrwl/schematics@7.8.0
+ @nestjs/cli@6.2.1

create-nx-workspace 명령으로 jamong이라는 플랫폼을 생성한다. SCSS 기반에 NestJS를 포함한 FullStack을 선택하자.  jamong 폴더 밑으로 apps/jamong Frontend 애플리케이션이 생성되었다. apps/api는 Backend 애플리케이션이다. ng s 명령을 수행하여 frontend, backend 서버를 각각 실행한다. frontend는 apps/jamong/proxy.conf.json 파일안에 proxy 경로로 /api가 설정되어 있다.

$ create-nx-workspace jamong --npm-scope=jm
$ cd jamong
$ ng s jamong (또는 ng serve jamong)
$ ng s api (또는 ng s api)

http://localhost:4200 을 호출한다. Dev Server와 API Server연결은 다음과 같다. 

 

plugin1 컴포넌트와 모듈을 생성한다. module을 생성하고 component를 생성하면 plugin1.module.ts의 declarations안에 자동으로 Plugin1Component가 설정된다. module 파일에 bootstrap 설정을 한다.

$ ng g module plugin1 --project=jamong
CREATE apps/jamong/src/app/plugin1/plugin1.module.ts (191 bytes)
$ ng g component plugin1 --project=jamong
CREATE apps/jamong/src/app/plugin1/plugin1.component.scss (0 bytes)
CREATE apps/jamong/src/app/plugin1/plugin1.component.html (26 bytes)
CREATE apps/jamong/src/app/plugin1/plugin1.component.spec.ts (635 bytes)
CREATE apps/jamong/src/app/plugin1/plugin1.component.ts (273 bytes)
UPDATE apps/jamong/src/app/app.module.ts (454 bytes)

// plugin1.module.ts
@NgModule({
  declarations: [Plugin1Component],
  imports: [
    CommonModule
  ],
  bootstrap: [Plugin1Component]
})
export class Plugin1Module { }

plugin module의 파일을 별도 파일로 번들링하기 위해 angular.json에서 "projects"/"jamong"/"architect"/"build"/"options" 안에 "lazyModules"설정을 한다.

  "projects": {
    "jamong": {
      "root": "apps/jamong/",
      "sourceRoot": "apps/jamong/src",
      "projectType": "application",
      "prefix": "jm",
      "schematics": {
        "@nrwl/schematics:component": {
          "style": "scss"
        }
      },
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/apps/jamong",
            "index": "apps/jamong/src/index.html",
            "main": "apps/jamong/src/main.ts",
            "polyfills": "apps/jamong/src/polyfills.ts",
            "tsConfig": "apps/jamong/tsconfig.app.json",
            "assets": ["apps/jamong/src/favicon.ico", "apps/jamong/src/assets"],
            "styles": ["apps/jamong/src/styles.scss"],
            "scripts": [],
            "es5BrowserSupport": true,
            "lazyModules": [
              "apps/jamong/src/app/plugin1/plugin1.module"
            ]
          },
    ... 중략 ...

"ng s jamong" restart하면 plugin1 모듈 파일이 별도 생성됨을 알 수 있다. 물리적은 파일로 build 하고 싶다면 "ng build jamong"을 수행한다. 

$ ng build jamong

다음으로 app2-jamong-src-app-plugin1-plugin1-module.js 파일을 Dynamic Loading하는 Loader인 lazy-af를 설치한다. lazy-af의 자세한 소스는 github에서 확인한다. lazy-af는 lazy.module.ts에 NgModuleFactoryLoader로 SystemJSNgModuleLoader를 설정해 사용한다.

$ npm install @herodevs/lazy-af

// https://github.com/herodevs/herodevs-packages/blob/master/projects/lazy/src/lib/lazy.module.ts
@NgModule({
  imports: [],
  declarations: [LazyAFComponent],
  exports: [LazyAFComponent],
  providers: [{ provide: NgModuleFactoryLoader, useClass: SystemJsNgModuleLoader }],
})
export class LazyModule {}

lazy-af관련 모듈을 apps/jamong/src/app/app.module.ts에 import 한다.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { LazyModule } from '@herodevs/lazy-af';

import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule, 
    HttpClientModule,
    LazyModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

apps/jamong/src/app/app.component.html 과 .ts 와 plugin1/plugin1.component.html 과 .ts을 수정한다. 

// app.component.html
<button (click)="loadPlugin()">Load plugin</button>
<lazy-af *ngIf="plugin1Path" [moduleName]="plugin1Path"></lazy-af>

// app.component.ts
@Component({
  selector: 'jm-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

  plugin1Path: string;

  loadPlugin() {
    this.plugin1Path = 'apps/jamong/src/app/plugin1/plugin1.module#Plugin1Module';
  }
}

// plugin1/plugin1.component.html
<div style="padding-top: 20px">
  Plugin 1
</div>
<div>Message: {{ hello$ | async | json }}</div>

// plugin1/plugin1.component.ts
import { Component } from '@angular/core';
import { Message } from '@jm/api-interface';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'jm-plugin1',
  templateUrl: './plugin1.component.html',
  styleUrls: ['./plugin1.component.scss']
})
export class Plugin1Component {
  hello$ = this.http.get<Message>('/api/hello');
  constructor(private http: HttpClient) { }
}

수정을 반영하고 실행을 한다. 

  1. Load plugin 버튼을 클릭한다.
  2. apps-jamong***.js 파일을 다운로드 한다. 
  3. Angular F/W 모듈을 해석한후 Plugin1Component를 렌더링한다.

지금까지의 소스 - github

다음 글에서 Multi Application의 Plugin 방식을 살펴보자.

 

<참조>

- nx development

 

Nx: Angular CLI power-ups for modern development

With Nx, you can develop multiple full-stack applications holistically and share code between them all in the same workspace. Add Cypress, Jest, Prettier, and Nest into your dev workflow.

nx.dev

- Angular Code Syntax와 유사한 Node 서비스 개발 프레임워크 NestJS

 

NestJS - A progressive Node.js web framework

NestJS is a framework for building efficient, scalable Node.js web applications. It uses modern JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reactive Progra

nestjs.com

- Angular Module Loader - lazy-af

 

@herodevs/lazy-af

This component allows you to lazily load your Angular module whenever you want, instead of being restricted to lazy loading on route changes.

www.npmjs.com

 

posted by 윤영식