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

Publication

Category

Recent Post

2018. 12. 27. 15:27 Angular/Architecture

Angular CLI v6.* 이후 Multi Application을 위한 Workspace개념이 도입되었다. 공통 Module을 통해 하나의 폴더안에서 여러 애플리케이션을 만들 수 있는 방법에 대해 알아본다. 




Schematics


스케메틱스는 workflow 툴이다. 

  - 프로젝트에 파일을 생성하고

  - 기존 코드를 변경하고

  - 환경 옵션을 추가하거나 프레임워크를 추가할 수 있다. 


스케메틱스는 Angular CLI의 코드 스케폴딩에 사용하며 다음 목표를 지향하여 개발되었다. 

  - 사용 및 개발이 쉬워야 한다

  - 확장 및 재상용성이 좋아야 한다

  - CLI동작시 부작용을 제거할 수 있어야 한다.

  - 비동기성을 지원한다.


Tree

  - 이미 존재하는 파일 관계를 작는 데이터 구조체이다. 

  - 파일을 수정하거나 생성하고 찾아 갈때 사용한다.

  - 스케메틱의 Start point가 된다. 

  - 하나의 스케메틱 Tree를 다른 스케메틱 Tree로 전달할 수 있다. 

  - create, delete, rename, overwrite 내가지 함수를 제공한다.


나만의 스케메틱스 만들기 

  - Node v6.9 이상 설치

  - schematics 실행환경을 만들기 위해 shcematics-cli를 설치한다. 

$ npm install -g @angular-devkit/schematics-cli

// 빈 스케메틱스 프로젝트 생성

$ schematics blank --name=my-project

$ cd my-project


Collections

  - 이름을 갖는 schematics의 집합이다. 이것은 사용자에 의해 발행되고 설치될 수 있다. 

  - 예로 @schematics/angular collection이 있다. 이것은 component, module, application 등에 대한 schematics를 가지고 있다. 

  - 생성한 my-project는 하나의 schematics만을 가지고 있고, my-project/src/collection.json 에 정의되어 있다. 

  - schematics key안에 하나의 my-project schematic 이 설정되어 있다.

  - factory key는 자바스크립트 펑션 위치를 설정한다. 이것은 RuleFactory라 한다. 

// src/collection.json

{

  "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",

  "schematics": {

    "my-project": {

      "description": "A blank schematic.",

      "factory": "./my-project/index#myProject"

    }

  }

}


// src/my-project/index.ts

import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';


export function myProject(_options: any): Rule {

  return (tree: Tree, _context: SchematicContext) => {

    return tree;

  };

}


Rule

  - Tree를 받아 Tree를 반환하는 함수이다. 

  - Rule이 스케메틱스의 핵심이다. 

  - Factory 함수는 입력 options 아규먼트를 받아 원하는 것을 처리 후 다시 Rule을 반환한다. 

  - 옵션은 CLI의 경우 command line 아규먼트로 전달되는 값이다. (보통 --name=<value> 같이 전달함)

 

사용자가 만든 스케메틱스를 수행하기 

  - dry-run은 스케메틱스 자신안에서 수행할 수 있게 한다. default는 false 이다.

  - 우리가 만든 스케메틱스는 애플리케이션안에서 사용하는 것이다. 따라서 자기 자신안에서 스케메틱스 검증을 해보려면 dry-run 옵션을 준다.

  - 만들어진 스케메틱스를 디버깅 할 수 있다. 

$ npm run build


// dry-run 수행시, 가급적 사용하지 말자, 별도의 temporary 폴더에서 dry-run없이 테스트 한다. 

$ schematics .:my-project --name=test --dry-run=true


// 디버깅

$ node --inspect-brk $(which schematics) .:my-project --name=test


다른 스케메틱스를 함께 사용하기 

  - chain을 통해 기존 @schamatics/angular의 component 생성하는 것을 이용한다. 

  - Angular component를 만든 다음 상단에 license 문구를 추가한다. 

import { Rule, SchematicContext, Tree, chain, externalSchematic } from '@angular-devkit/schematics';


const licenseText = `

/**

 * @license

 * Copyright Google Inc. All Rights Reserved.

 *

 * Use of this source code is governed by an MIT-style license that can be

 * found in the LICENSE file at https://angular.io/license

 */

`;


export function myProject(options: any): Rule {

  return chain([

    externalSchematic('@schematics/angular', 'component', options),

    (tree: Tree, _context: SchematicContext) => {

      tree.getDir(options.sourceDir)

          .visit(filePath => {

            if (!filePath.endsWith('.ts')) {

              return;

            }

            const content = tree.read(filePath);

            if (!content) {

              return;

            }


            // Prevent from writing license to files that already have one.

            if (content.indexOf(licenseText) == -1) {

              tree.overwrite(filePath, licenseText + content);

            }

          });

      return tree;

    },

  ]);

}


수행해 보자.

// my-project schematics 폴더안에서 의존관계있는 @schematics/angular 설치

$ npm install --save @schematics/angular

$ npm run build


// 신규 프로젝트 생성하고, my-project schematics 를 npm link 건다.

$ cd ../

$ ng new <otherProjectName>

$ cd <otherProjectName>

$ npm link <my-project schematics path>


// schematics:schematic 을 호출한다. angular module의 declarations에도 자동 추가된다. 

$ ng g my-project:my-project

? What name would you like to use for the component? hi

CREATE src/app/hi/hi.component.css (0 bytes)

CREATE src/app/hi/hi.component.html (21 bytes)

CREATE src/app/hi/hi.component.spec.ts (600 bytes)

CREATE src/app/hi/hi.component.ts (253 bytes)

UPDATE src/app/app.module.ts (537 bytes)




Multi Application을 위한 Nx Workspace


Nx Workspace는 nrwl에서 만든 multi application 개발을 위한 Angular CLI에 대한 확장팩이다. 다음 3가지의 향상시키기 위해 만들어졌다. 

  - 생산성: 초시 셋업시간을 절약한다.

  - 일관성: 기업용 애플리케이션을 위한 좀 더 향상된 코드 제너레이터를 제공하고, 일관된 물리적 공간과 컨벤션을 제공한다.

  - 안정성: 분석기능 강화




Workspace에서 작업하기

  - 단일 레파지토리에서 Multi Application과 Library에 대한 개발을 수행한다.

    + Unified versioning: 하나의 레파지토리 사용에 따른 

    + Promotes code sharing and reuse: lib모듈을 통한 공유

    + Easier dependency management: 하나의 node_modules만 사용, 하나의 build setup

    + Refactoring benefits: code editors 지원, 변경이 전체 애플리케이션에 반영

    + Consistent developers experience: 공유 코드 변경에 대한 전체 애플리케이션 유효성 체크 가능



Nx Workspace 만들기

create-nx-workspace 명령어를 통해 nx 기반 angular workspace를 생성한다. 마치 create-react-app과 같은 역할이다. 

  - npmScope: 그림처럼 NPM Scope로 library를 접근하는 @my-company와 같은 npm scope를 설정


예로 @angular가 npm scope이다. 


  - directory workspace의 폴더명 변경

$ npm i -g @nrwl/schematics  @angular/cli


// command: create-nx-workspace <workspace name> --directory=<workspace별칭폴더명> --npm-scope=<library접근 scope>

$ create-nx-workspace my-project-suite --npm-scope=apple




Multi Application & Library 생성하기


Nx Workspace에 application과 library를 생성한다. Angular CLI Workspace에서 만들어 지는 폴더 구조와 다음과 같은 차이가 있다. 

  - apps: apps폴더 밑으로 Application별 폴더가 존재한다. 

  - libs: application에서 공유하는 Angular Module별 폴더가 존재한다.


애플리케이션 프로젝트에 대한 정보는 angular.json에서 확인할 수 있다. 

{

  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",

  "version": 1,

  "newProjectRoot": "",

  "projects": {}, <-- 프로젝트 즉, 애플리케이션은 아직 등록되지 않았다. 

  "cli": {

    "warnings": {

      "typescriptMismatch": false,

      "versionMismatch": false

    },

    "defaultCollection": "@nrwl/schematics", <-- nrwl/schematics를 사용한다. 

    "packageManager": "npm"

  }

}


nx.json은 Nx Workspace특화된 환경값을 갖는다. 

{

  "npmScope": "apple",

  "implicitDependencies": {

    "angular.json": "*",

    "package.json": "*",

    "tsconfig.json": "*",

    "tslint.json": "*",

    "nx.json": "*"

  },

  "projects": {}

}


그외 tsconfig.json에 paths와 tslin.json의 nx-enforce-module-boundaries 설정, 그리고 package.json에 Nx를 위한 별도 script들이 존재한다. 


apps폴더 밑으로 프로젝트 추가하기

  - 명령: ng g app <application name>

  - ng g app --help 로 생성 옵션을 볼 수 있다. 예로 --routing 옵션을 주면 routing module이 자동 설정된다. 또는 생성시 yes 를 선택해되 된다. 

$ ng g app app-one


// 다음을 선택했다. 

? In which directory should the application be generated?

? Would you like to add Angular routing? Yes

? Which stylesheet format would you like to use? SCSS   [ http://sass-lang.com]

? Which Unit Test Runner would you like to use for the application? Jest   [ https://jestjs.io ]

? Which E2E Test Runner would you like to use for the application? Cypress[ https://www.cypress.io ]

? Which tags would you like to add to the application? (used for linting)


애플리케이션 app-one을 위해 다음과 같은 두개 폴더가 생성된다. 


만들어진 애플리케이션을 실행해 보자. 

  - ng serve <application name>

  - default port는 4200이다. 여러개의 애플리케이션을 동시 실행하고 싶다면 --port 옵션으로 포트를 서로 틀리게 설정한다. 또는 angular.json에 설정할 수 있다. 

$ ng serve app-one


libs폴더 밑으로 공유 라이브러리 추가하기

  - 라이브러리는 Angular Module이다.

  - ng g lib <library name> 

$ ng g lib adk

? In which directory should the library be generated?

? Which module should import the library?

? Would you like to add a routing configuration to the library? No

? Will this library be lazy loaded? No

? Would you like to generate an NgModule within the library? Yes

? Which tags would you like to add to the library? (used for linting)

? Which Unit Test Runner would you like to use for the library? Jest (https://jestjs.io/)


생성을 하게되면 libs/adk 폴더가 생성되고, angular.json에 projectType으로 library가 자동 등록된다. (nx.json에도 자동 등록됨)


lazy loading되는 library module 생성

  - --routing이 설정된 모듈

  - lazy loading되는 상위 모듈: 어차피 애플리케이션도 라이브러리도 모듈이다.

  - --lazy 옵션을 직접주거나 아래처럼 선택할 수 있다. 

$ ng g lib login --routing --parent-module=./apps/app-one/src/app/app.module.ts


? In which directory should the library be generated?

? Will this library be lazy loaded? Yes

? Would you like to generate an NgModule within the library? Yes

? Which tags would you like to add to the library? (used for linting)

? Which Unit Test Runner would you like to use for the library? Jest (https://jestjs.io/)


routing 설정을 하게되면 login.module.ts와 app-one의 app.module.ts 안의 설정은 다음과 같다. 

  - @apple 이라는 npm scope가 자동으로 붙는다. 

// libs/login/src/lib/login.module.ts

@NgModule({

  imports: [

    CommonModule,


    RouterModule.forChild([

      /* {path: '', pathMatch: 'full', component: InsertYourComponentHere} */

    ])

  ]

})

export class LoginModule {}


// apps/app-one/src/app/app.module.ts

@NgModule({

  declarations: [AppComponent],

  imports: [

    BrowserModule,

    NxModule.forRoot(),

    RouterModule.forRoot(

      [{ path: 'login', loadChildren: '@apple/login#LoginModule' }],

      { initialNavigation: 'enabled' }

    )

  ],

  providers: [],

  bootstrap: [AppComponent]

})

export class AppModule {}


루트의 tsconfig.json에는 library module에 대한 paths 정보가 자동 설정된다. 

    "lib": [

      "es2017",

      "dom"

    ],

    "baseUrl": ".",

    "paths": {

      "@apple/adk": [

        "libs/adk/src/index.ts"

      ],

      "@apple/login": [

        "libs/login/src/index.ts"

      ]

    }




Multi Application 빌드하기 


angular.json의 architect에는 build와 serve에 대한 환경설정이 있다.

      "architect": {

        "build": {

          "builder": "@angular-devkit/build-angular:browser",

          "options": {

            "outputPath": "dist/apps/app-one",

            "index": "apps/app-one/src/index.html",

            "main": "apps/app-one/src/main.ts",

            "polyfills": "apps/app-one/src/polyfills.ts",

            "tsConfig": "apps/app-one/tsconfig.app.json",

            "assets": [

              "apps/app-one/src/favicon.ico",

              "apps/app-one/src/assets"

            ],

            "styles": [

              "apps/app-one/src/styles.scss"

            ],

            "scripts": []

          },

          "configurations": {

            "production": {

              ....

             }

           }


빌드 명령

애플리케이션을 빌드한다

  - ng build <application name> --prod

  - nx workspace command 목록

$ ng build app-one --prod


라이브러리를 빌드하려면 라이브러리 생성할 때 --publishable 옵션을 주어서 생성한다. 그러면 angular.json파일의 "architect"에 "build"옵션이 추가된다. library build할 때는 ng-packagr를 이용하여 어디에서든 사용할 수 있는 APF v6가 적용된다. APF는 Angular Package Format의 약어이다. 

 $ ng g lib ui --publishable --routing --parent-module=./apps/app-one/src/app/app.module.ts


// angular.json 

    "ui": {

      "root": "libs/ui",

      "sourceRoot": "libs/ui/src",

      "projectType": "library",

      "prefix": "apple",

      "architect": {

        "build": {

          "builder": "@angular-devkit/build-ng-packagr:build",

          "options": {

            "tsConfig": "libs/ui/tsconfig.lib.json",

            "project": "libs/ui/ng-package.json"

          }

        },


Angular Pakage Format v6 지원 목록


ui라는 라이브러리 빌드하기 

$ ng build ui

Building Angular Package

Building entry point '@apple/ui'

Compiling TypeScript sources through ngc

Bundling to FESM2015

Bundling to FESM5

Bundling to UMD

Minifying UMD bundle

Copying declaration files

Writing package metadata

Removing scripts section in package.json as it's considered a potential security vulnerability.

Built @apple/ui

Built Angular Package!

 - from: /Users/dowonyun/mobicon/projects/bistel/prototyping/my-project-suite/libs/ui

 - to:   /Users/dowonyun/mobicon/projects/bistel/prototyping/my-project-suite/dist/libs/ui


// 다양한 번들 파일이 생성됨 



$schema와 schema.json 개념

Angular Workspace에 대한 schema를 강제하기 위하여 schema.json 파일을 angular.json의 $schema에 설정한다. 

  - Angular Workspace Schame에 대한 Json 스펙

  - Angular CLI의 Schematics 에 목록: 각 폴더 밑에 schema.json 파일이 존재한다. 

  - schematics 키의 값은 workflow를 수행할 때 적용할 옵션을 설정할 수 있다. 

     지정가능한 옵션 목록

"schematics": {

        "@schematics/angular:component": {

          "styleext": "scss",

          "changeDetection": "OnPush",

          "viewEncapsulation": "None",

          "export": true

        }

},




<참조>

- Schematics 소개

- 2018 FEConf, 고재도님의 Schematics 소개영상

- Angular CLI의 Workspace 과 Schematics 개념

- Angular CLI command 목록

- Nrwl Nx Workspace  홈페이지

- Nx Github 

- Nx Workspace 온라인 강좌 (Free)

- Angular Package Format(APF) 개념 강좌

- APF를 만들어주는 ng-packagr 소스 및 설명

- APF v6 스펙

- ng-packagr 소개

posted by 윤영식