ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Angular 2 ~ 4 개발 스타일 가이드
    Angular 2017. 10. 17. 17:53



    굉장히 오랜만에 글을 올립니다.

    원래라면 이전 angular tutorial 을 마무리 지으려 했지만, 그 간 angular4 도 나오고 시간이 많이 지났네요.

    결정적인건 노트북을 새로 사면서 설명을 적어두었던 코드들이 사라지는 바람에 그냥 패스 하겠습니다(?).


    그 대신에 새로운 프로젝트로 연습 해보려 합니다.

    아마 이번 프로젝트로 angular 의 기본 스킬과 구글의 Material Design , 그리고 angular 에서 가장 중요한 부분 중 하나인

    Rxjs 의 사용법에 대해 상세하고 쉽게 설명하려 합니다.


    그전에 저 역시 회사에서 angular 를 이용한 프로젝트를 하고 있지만, 많은 분들이 놓치는 부분이 있어서 그에 대해 

    구글이 직접 제시한 angular style guide 를 정리 해 봅니다. 


    해당 style guide 를 진행하면서, 실제로 실무에서 사용했을 때, 변경 될 점이나 필요한 부분은 추가 했습니다.

    그냥 이건 쉽게 말하면 이거다 라는 개념으로 적었습니다.

    만약 angular 로 프로젝트를 진행 중 이라면 아래 가이드를 보며 얼마나 지키고 있는지 세어 보는 것도 

    좋을 것 같습니다.




    Rule of One

    Style 01 - 01



    * 파일을 작성할 때 하나의 파일에는 하나의 구성요소 관련 코드만 작성하세요.

    - 이 말은 즉, Component 파일에는 Component 에 관련된 코드만 작성하고, 

      Service 파일에는 Service 에 관련된 코드만 작성 하라는 뜻 입니다.

    - 가장 기본적인 부분이라 설명하지 않아도 다 아실거라 생각합니다.



    Don't do this 

    import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule, Component, OnInit } from '@angular/core';
    class Hero {
    id: number;
    name: string;
    }
    @Component({
    selector: 'my-app',
    template: `
    <h1>{{title}}</h1>
    <pre>{{heroes | json}}</pre>
    `,
    styleUrls: ['app/app.component.css']
    })
    class AppComponent implements OnInit {
    title = 'Tour of Heroes';
    heroes: Hero[] = [];
    ngOnInit() {
    getHeroes().then(heroes => this.heroes = heroes);
    }
    }
    @NgModule({
    imports: [ BrowserModule ],
    declarations: [ AppComponent ],
    exports: [ AppComponent ],
    bootstrap: [ AppComponent ]
    })
    export class AppModule { }
    platformBrowserDynamic().bootstrapModule(AppModule);
    const HEROES: Hero[] = [
    {id: 1, name: 'Bombasto'},
    {id: 2, name: 'Tornado'},
    {id: 3, name: 'Magneta'},
    ];
    function getHeroes(): Promise<Hero[]> {
    return Promise.resolve(HEROES); // TODO: get hero data from the server;
    }


    - 위 코드를 보니 뭐가 문제인지 보이죠?  컴포넌트 파일안에 Module,. Service, Model 등 모든 구성요소를

      꾹꾹 눌러 담았습니다. 

    - 각 구성요소는 Module, Service, Model 등 으로 알맞게 분리해서 작성하도록 합시다.





    Small functions

    Style 01-02


    * 함수를 작성 할 때 함수의 크기를 작게 만드세요.


    - 여러 기능이 있는 한개의 함수 크기를 줄이고 특정목적을 가지는 작은 함수로 

      쪼개어 지면 해당 함수를 테스트 하기 쉽습니다.

    - 특정목적을 가진 작은 함수들은 재사용하기 쉽습니다. 

      (물론, 해당 함수가 해당 프로그램에서 특정한 곳에서 단 한번만 쓰인다면 궂이 쪼갤 필요는 없습니다.)

    - 작은 크기의 함수는 파악하기 쉽습니다.

    - 작은 크기의 함수는 유지하기가 쉽습니다.

    - 음 이건 함수형 프로그래밍이 유용해 보이는 군요 






    -------- Naming Part



    General Naming Guidelines

    Style 02-01


    * 모든 기호(?)에는 일관된 이름을 사용하세요. 

    - 하나의 패턴을 정하여 일관성 있게 기호의 이름을 작성하십시오.

    - 권장되는 패턴은 featuare.type.ts  입니다.

    - 이런 일관적인 패턴은 프로젝트 와 팀과의 협동 작업 에서도 많은 효율을 가져다 줍니다.

    - 하나의 패턴으로 작성된 명명규칙은 코드를 더 빨리 찾아내고 이해하기 쉽습니다.


    example) 

    hero-list.component.ts    =  해당 컴포넌트는 영웅목록을 나타내는 작업을 한다.




    Separate file names with dots and dashes

    Style 02-02


    * 파일 이름은 점 ( . ) 과 대시 ( - ) 로 구분하십시오.

    - 여기서 . 과 - 를 이용한다는 것은 해당 파일의 설명은 - 로 , 유형은 . 으로 분리하여

      파일이름을 작성하십시오. 

    - 구성요소의 파일이름을 작성할 때도 일관된 패턴을 사용하는 것이 혼동을 주지 않고,

       작업의 효율성을 높여 줍니다.

    - .service, .component, .pipe, .module 및 .directive 와 같은 기존 유형이름을 적극적으로 사용하고,

       추가로 너무 많은 것을 만들지 않도록 주의해야 합니다.


    example)

    hero-list.component.ts   =   hero-list  는 '-' 로 hero 와 list 를 분리하여 영웅의 목록기능임을 구분하였고,

     .component 는 '.' 으로 컴포넌트 요소 임을 구분하였다.





    Symboles and file names

    Style 02-03 

    - 기호의 이름을 파일 이름과 일치시켜 작성하십시오.

    - 해당 파일의 Class , Directive, Module , Pipe 등의 이름을 파일이름과 일치 시켜야 합니다.

    - 위 와 비슷한 이야기 입니다. 그냥 예시를 보죠.


    Symbol NameFile Name
    @Component({ ... })
    export class AppComponent { }

    app.component.ts

    @Component({ ... })
    export class HeroesComponent { }

    heroes.component.ts

    @Component({ ... })
    export class HeroListComponent { }

    hero-list.component.ts

    @Component({ ... })
    export class HeroDetailComponent { }

    hero-detail.component.ts

    @Directive({ ... })
    export class ValidationDirective { }

    validation.directive.ts

    @NgModule({ ... })
    export class AppModule

    app.module.ts

    @Pipe({ name: 'initCaps' })
    export class InitCapsPipe implements PipeTransform { }

    init-caps.pipe.ts

    @Injectable()
    export class UserProfileService { }

    user-profile.service.ts


    - 보시면 파일이름과 Class 이름을 일치 시킨 부분이 보입니다.






    Service names 

    Style 02-04


    * 서비스의 이름은 해당 기능의 이름을 따서 일관되게 작성하십시오.

    - 서비스의 이름을 작성 할때 Service 접비사를 추가 하십시오.

    - 예를 들면 데이터를 얻는 서비스는 DataService , 영웅에 관련된 서비스는 HeroService 라고 해야 합니다.

    - 예외적인 서비스의 이름도 있습니다. Logger 와 같이 Log 메시지를 기록하는 명확한 서비스는

       LoggerService 보단 Logger 가 적합 할 수도 있습니다.

    - 해당 예시를 봅시다.


    Symbol NameFile Name
    @Injectable()
    export class HeroDataService { }

    hero-data.service.ts

    @Injectable()
    export class CreditService { }

    credit.service.ts

    @Injectable()
    export class Logger { }

    logger.service.ts



    Bootstrapping

    Style 02-05


    * 해당 서비스의 Bootstraping 및 플랫폼 로직은 main.ts 파일에 저장 하십시오.

    - bootstraping 작업에 오류처리를 포함시켜야 합니다.

    - main.ts 에는 앱과 관련된 작업을 포함시키지 마십시오. 

      앱과 관련된 작업은 Component 또는 Service 에 배치해야 합니다.


    example)  

    main.ts

    import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

    import { AppModule } from './app/app.module';

    platformBrowserDynamic().bootstrapModule(AppModule)
    .then(success => console.log(`Bootstrap success`))
    .catch(err => console.error(err));


    - main.ts 파일에는 bootstraping 관련 작업만이 존재하고 있습니다.




    Directive selectors 

    Style 02-06


    * Directive 의 이름을 작성 할 때는 camelCase 방식을 사용하십시오.

    - 뷰에 바인딩 된 지시문에 정의된 속성의 이름을 특성 이름과 일관되게 합니다.

    - Angular 의 Html 구문 분석에서는 대소문자를 구분하기 때문에 camelCase 를 

      더욱 효율적으로 사용가능합니다.

    - camelCase 표기법은 쉽게 말하면 첫 글자는 소문자로 , 해당 단어가 바뀌거나, 구분을 

      줘야 할때 첫글자를 대문자로 씁니다. 

    example)  camelCase, superCar, googleDeveloper




    Custom prefix for components

    Style 02-07


    * Component 의 selector 이름은 하이픈이 포함된 값을 사용 하세요.

    - '-' 을 사용하여 selector 이름을 사용하십시오. 

    - 예를 들면 'admin-users' 라는 selector 을 사용하면 해당 component 는 admin 에서 users 를 보여주는

       컴포넌트라는 것이 명확하게 보입니다.

    - 이런식의 명명은 다른 앱에서 구성요소를 공유하고, 이름의 충돌을 방지하는데 유용합니다.



    wrong example )

    /* avoid */

    // HeroComponent is in the Tour of Heroes feature
    @Component({
    selector: 'hero'
    })
    export class HeroComponent {}


    convert example )

    @Component({
    selector: 'toh-hero'
    })
    export class HeroComponent {}




    Custom prefix for directives

    Style 02-08


    * Directive 를 작성 시 기능에 맞는 접두사를 사용하십시오.

    - 위 selector 와 같이 Directive 를 작성할 때도 해당 기능 관련 접두사를 붙이십시오.

    - 간단하게 예를 봅시다.


    wrong example)

    @Directive({
    selector: '[validate]'
    })
    export class ValidateDirective {}


    convert example)

    @Directive({
    selector: '[tohValidate]'
    })
    export class ValidateDirective {}




    Pipe names

    Style 02-09


    * Pipe 의 이름을 작성 할때도 해당 기능의 이름을 따서 일관되게 작성하십시오.

    - 바로 예를 봅시다.

    Symbol NameFile Name
    @Pipe({ name: 'ellipsis' })
    export class EllipsisPipe implements PipeTransform { }

    ellipsis.pipe.ts

    @Pipe({ name: 'initCaps' })
    export class InitCapsPipe implements PipeTransform { }

    init-caps.pipe.ts




    Unit test file names

    Style 02-10 


    * unit test 파일의 이름을 일관되게 작성하십시오.

    - 해당 component 의 이름과 비슷하게 작성하십시오.

    - 파일에는 .spec 를 붙여서 작성하십시오,

    - angular cli 에서 권장하는 테스트 프레임워크 karma 에서 test 동작 시 

      .spec 패턴을 매칭해서 사용됩니다. 

    - 해당 예를 봅시다.


    Test TypeFile Names

    Components

    heroes.component.spec.ts

    hero-list.component.spec.ts

    hero-detail.component.spec.ts

    Services

    logger.service.spec.ts

    hero.service.spec.ts

    filter-text.service.spec.ts

    Pipes

    ellipsis.pipe.spec.ts

    init-caps.pipe.spec.ts





    End-to-End (E2E) test file names 

    Style 02-11


    * E2E test 파일의 이름은  unit test 파일과 같이 .e2e-spec 을 붙여서 파일을 생성하세요.

    - unit test 와 같은 이유로 .e2e-spec 패턴을 자동으로 매칭시켜 테스트 합니다

    - 예를 봅시다.


    Test TypeFile Names

    End-to-End Tests

    app.e2e-spec.ts

    heroes.e2e-spec.ts


     


    Angular NgModule names 

    Style 02-12


    * 모듈 파일의 이름에는 .module 을 붙이고 모듈의 이름은 해당기능이나 폴더의 이름을 따서 작성하세요.

    - .module 패턴은 Angular Provider 에서 빠르게 모듈을 인식하는데 도움을 줍니다.

    - 해당 기능과 폴더 이름의 모듈파일은 해당 모듈의 기능을 쉽게 인식하는데 도움을 줍니다.



    * Routing Module 같은 경우 Class name 에 RoutingModule 을 붙이고 해당 RoutingModule 의 파일 이름은

      -routing.module.ts 을 붙이십시오.

    - Routing Moudle 의 일관된 클래스 이름과 파일 이름은 Router 를 쉽게 확인 할 수 있도록 도움을 줍니다.


    Symbol NameFile Name
    @NgModule({ ... })
    export class AppModule { }

    app.module.ts

    @NgModule({ ... })
    export class HeroesModule { }

    heroes.module.ts

    @NgModule({ ... })
    export class VillainsModule { }

    villains.module.ts

    @NgModule({ ... })
    export class AppRoutingModule { }

    app-routing.module.ts

    @NgModule({ ... })
    export class HeroesRoutingModule { }

    heroes-routing.module.ts


      


    ------ Coding conventions  


    Classes

    Style 03-01


    * 클래스의 이름은 Upper Camel case 형식으로 작성 하십시오.

    - 전통적인 클래스 이름 명명 방식을 따릅시다.

    - Class 는 인스턴트화 가 가능합니다. Upper Camel Case 로 명명되었다면 이는 

      인스턴스화 가능한 자산이라는 표시입니다. 

     (이 부분은 예전부터 Class 의 이름을 작성할 때 전통적으로 upper camel case 를 사용했고 

      해당 규칙으로 적힌 이름은 인스턴스화 즉 새로운 인스턴트를 생성 할 수 있는 자원이다 라는

      표시 인가 봅니다.)

    - 굳이 예를 볼 필요가 있나 싶지만 아래 예를 보면 첫글자는 대문자로 되어 있는 것을 확인 할 수 

      있습니다.


    export class exceptionService { constructor() { } }



    ex) 

    export class ExceptionService { constructor() { } }




    Constants

    Style 03-02


    * 변수를 지정 할때는 되도록이면 const 를 사용하여 생성하십시오.

    - const 로 지정된 변수는 직접적으로 변경시킬 수 없기 때문에 해당 변수의 무결성을 지키기에 좋은 선택이고, 

      TypeScript 는 이를 지킬 수 있도록 많은 도움을 줍니다.


    * const 로 생성되는 변수의 이름은 lower camel case 형식을 사용하십시오.

    - lower camel case 로 작성된 이름은 기존 UPPER_SNAKE_CASE 보다 읽기 쉽고, 식별하기 용이합니다.


    * UPPER_SNAKE_CASE 형식의 지정은 제 3자의  라이브러리, 모듈에서 사용됩니다.

    - 타 라이브러리나 모듈과 구별을 위해서 되도록이면 lower camel case 형식을 사용하십시오.





    Interfaces 

    Style 03-03


    *Interface 의 이름은 upper camel case 형식을 사용하십시오. 

    - Interface 의 이름에서 'I' 형식은 사용하지 마십시오.

    - 되도록 인터페이스 보단 Class 를 사용하십시오. 

    - Interface 의 'I' 명명 방식은 Typescript 에서 이를 인식하는데 방해를 줍니다.

    - Class 하나를 작성하는 것이 class + interface  조합보다 코드가 더 적습니다.

    - Class 는 Interface 형식으로 작동하는 것이 가능합니다. ( extends 가 아닌 implements 를 사용)




    Properties and methods

    Style 03-04


    * 속성과 함수의 이름은 lower camel case 로 작성하십시오.

    - 특히 변수와 함수의 이름에 '_' 를 붙이는 것은 삼가하십시오. 


    ex)

    private _toastCount: number; (x)

    private toastCount: number; (o)


    - 속성과 함수의 일반적인 생각을 따르십시오. (뭔 소리야?)

    - TypeScript 는 private 과 public 을 지원하기에 private 과 public 의 구분이 

      아주 쉽습니다.  굳이 '_' 를 사용하면서 속성, 변수의 이름에 변화를 주지 않아도 

       됩니다.




    Import line spacing

    Style 03-06


    * 외부의 기능,모듈을 import 를 작성할 때 코어 기능과 제 3자의 기능은 공백을 사용하여 구분하십시오.

    * 모듈 별 , 알파벳 순으로 작성하는 것을 고려 하십시오.

    * 내부 기능,모듈의 이름도 알파벳 순으로 작성 하는 것을 고려 하십시오.


    - 예를 봅시다.

    import { Injectable } from '@angular/core'; import { Http } from '@angular/http';


    import { Hero } from './hero.model'; import { ExceptionService, SpinnerService, ToastService } from '../../core';


    - 위 예를 보면 코어 모듈과 일반적인 제 3자의 모듈이 공백으로 분리 되었습니다.

    - 구분된 모듈,기능들은 알파벳 순으로 작성 하였습니다.

    - 마지막 줄 서비스의 import 를 보면 내부 service 역시 알파벳 순으로 작성하였습니다.


    - 해당 방식으로 작성시의 이점은 굳이 적지 않아도 아시겠죠?






    ----- Application structure and NgModules 

    Application 의 구조와 모듈 작성에 관련된 guide 입니다.



    LIFT

    Style 04-01


    * 코드를 빠르게 찾을 수 있도록 코드를 작성하고, 가벼운 구조를 유지 해야 합니다.

    * 이를 위해  LIFT 기본 지침을 따르도록 하십시오.

    (LIFT = Locate, Identify, Flat, T-DRY (Try to DRY)




    Locate

    Style 04-02


    * 직관적이고 간단하고 신속하게 코드를 찾을 수 있어야 합니다.

    -  작업할 때 만약 파일이름을 모르거나 기억하지 못하는 경우 파일을 빠르게 찾을 수 있어야 합니다.

    -  그러기 위해선 폴더 구조를 누가 봐도 쉽게 찾을 수 있도록 구조화 해야 합니다.



    Identify

    Style 04-03


    * 파일의 이름은 파일에 무엇을 포함하고 나타내는 지 알 수 있도록 작성하십시오.

    * 파일의 구성요소는 하나로 유지하십시오.

      (이 말은 하나의 파일에 여러가지 서비스나 기능을 혼합하지 말라는 뜻 입니다.)

    * 코드를 효율적으로 찾기 위해서라면 짧으면서 축약된 이름보다 차라리 긴 파일 이름이 

      훨씬 낫습니다.



    Flat

    Style 04-04


    * 가능한 한 flat 한 폴더 구조를 유지 하십시오.

    - 폴더내에 파일이 7개 이상이 되면 하위 폴더를 작성합니다.

    - 폴더내에 .js 나 .js.map 파일과 같은 관련이 없는 파일은 숨기도록 IDE 를 구성해야 합니다.

    - 심리학자들은 사람에게 흥미로운 것들이 9개를 초과하면 인간의 고민은 더 깊어 진다고 합니다.

       이에 따라 폴더에 10개 이상의 파일이 되었을 때 서브 폴더를 만들어도 됩니다. :)

       서브 폴더에 관련된 건 전적으로 작성자의 결정에 달려있습니다.



    D-TRY (Try to be DRY)

    Style 04-05


    * DRY 하게 만들기 위해 약간의 가독성을 희생하십시오.

    * 예를 들면 .html 확장자를 사용하면 이 파일은 분명히 View 파일 인 것이 확실합니다.

      때문에 해당 파일의 이름을 굳이 hero-view.component.html 로 지정 할 필요는 없습니다.

      다만 이런요소들이 LIFT 의 다른요소를 방해한다면 DRY 한 규칙은 지켜지지 않아도 됩니다.


    ( guide 에는 이렇지만, 저 같은 경우 component 의 구성요소 파일들의 이름은 되도록이면 

      똑같이 명명하는 것을 추천합니다. )

    ex) hero-view.component.ts , hero-view.component.html , hero-view.component.scss



    Overall structural guidelines

    Style 04-06


    * application 의 구조는 처음에는 작게 시작하십시오. 하지만 언제나 application 의 방향은 생각해야 합니다.

    * app 의 모든 코드는 src 폴더 안에 작성 하십시오.

    * .ts , .html , .css , .spec 의 셋트가 완성되었다면 해당 기능의 폴더를 만드십시오.

    - app 의 구조는 처음에는 작고 유지하기 쉽도록 하고, 앱이 성장하면서 쉽게 진화할 수 있도록 합니다.

    - 보통 하나의 구성요소에는 4개의 파일이 있으며 (.ts, .html, .css, .spec.ts) , 이들을 각각 폴더에 넣을 경우

      폴더를 빠르게 정리 할 수 있습니다.



    <폴더 구조 예제>

    --------------------------------------------------------------------------------------------------------------------------------------------

    <project root>
    src
    - app
    - core
    core.module.ts
    exception.service.ts|spec.ts
    user-profile.service.ts|spec.ts
    - heroes
    - hero
    hero.component.ts|html|css|spec.ts
    - hero-list
    hero-list.component.ts|html|css|spec.ts
    - shared
    hero-button.component.ts|html|css|spec.ts
    hero.model.ts
    hero.service.ts|spec.ts
    heroes.component.ts|html|css|spec.ts
    heroes.module.ts
    heroes-routing.module.ts
    - shared
    shared.module.ts
    init-caps.pipe.ts|spec.ts
    text-filter.component.ts|spec.ts
    text-filter.service.ts|spec.ts
    - villains
    - villain
    ...
    - villain-list
    ...
    - shared
    ...
    villains.component.ts|html|css|spec.ts
    villains.module.ts
    villains-routing.module.ts
    app.component.ts|html|css|spec.ts
    app.module.ts
    app-routing.module.ts
    main.ts
    index.html
    ...
    - node_modules/...
    ...

    -------------------------------------------------------------------------------------------------------------------------------------------------

    - 큰 주제의 폴더를 만들고 해당 폴더 내에 기능별 폴더를 정리합니다.

    - heroes 의 예를 보면 큰 주제인 heroes 를 만들고 heroes 의 메인 구성요소는 heroes 폴더,

      hero 의 데이터 관리 기능은 hero 폴더, hero 의 리스트를 보여주는 구성요소는 

      hero-list 폴더에 있습니다.

      shared 폴더에는 heroes 에서 공유하여 사용하는 서비스 , 라우팅, 모델등의 파일이 있습니다.



    Folders-by-feature-structure

    Style 04-07


    * 폴더가 나타내는 기능들의 집합의 이름이 지정된 폴더를 만듭니다.

    - 위 폴더 예시에서 말했듯이 heroes 폴더를 만들어 hero 에 관련된 기능은 heroes 

       폴더에 넣는 식으로 폴더를 구조화 합니다.

    - 위에 설명한 LIFT guide 가 이런 구조를 작성하는데 도움이 됩니다.


    * 해당 주제 마다 module 파일을 만드십시오.

    - ngModule 을 사용하면 lazy loading 을 가능케 하며 , 이는 app 의 속도를 빠르게 합니다.

    - ngModule 은 해당 기능들의 독립성을 유지하며, 기능들의 테스트, 재사용을 용이하게 합니다.



    App root module

    Style 04-08


    * 응용프로그램의 루트폴더에 루트모듈 파일을 만듭니다.

    - 모든 응용프로그램은 하나이상의 루트 모듈파일이 필요합니다.

    - 루트모듈 파일의 이름은 app.module.ts 로 지정하는 것을 추천합니다.

    - src/app 폴더에 app.module.ts 파일을 만든다면 루트 모듈을 찾고 식별하기 쉽습니다.



    Feature modules 

    Style 04-09



    * 독립된 모든 기능에는 관련 모듈을 만들어야 합니다.

    * 만들어진 기능모듈은 기능집합 폴더와 같은 곳에 위치 해야 합니다. 

      (ex: app/heroes  내부에 heroes 관련 모듈 작성)

    * 만들어진 기능관련 모듈의 파일 이름은 기능의 이름을 반영해야 합니다.

      (ex: app/heroes/heroes.module.ts)

    * 기능모듈의 모듈이름은 기능, 파일 모듈과 관련된 이름으로 지정합니다.

     (ex: app/heroes/heroes.module.ts 의 정의는 HeroesModule )


    - 독립적으로 만들어진 기능 모듈은 다른 모듈에서 import 하거나 자체적으로 숨길 수 있습니다.

    - 기능 모듈은 해당 기능을 구성하는 관련 구성요소를 한 곳에 묶어 식별하기 쉽도록 합니다.

    - 기능 모듈은 라우팅을 효율적으로 구성할 수 있고, lazy loading 으로 구성할 수 도 있습니다.

    - 기능 모듈은 해당 기능과 특정 기능을 명확하게 구분합니다.

    - 기능 모듈을 사용하면 여러 팀이 분산해서 작업하기 쉽습니다.

    - 기능 모듈은 독립성을 쉽게 유지할 수 있고, 테스트 하는 것도 용이합니다.



    Shared feature module

    Style 04-10


    * 공유 폴더에 SharedModule 이라는 기능 모듈을 작성합니다.

      (ex: app/shared/shared.module.ts 라는 공유모듈을 정의합니다.)

    * 만들어진 구성요소 나 기능, 지시문, 서비스 등이 재사용 되고 참조 될 때 

      해당 항목은 공유 기능으로 분류합니다.

    * 공유기능 모듈에서는 필요한 모든 모듈을 참조합니다. 

      (ex: Common Module, Forms Module )

    * SharedModule 에서는 모든 구성요소, 지시문 및 파이프를 참조합니다. 

    * SharedModule 에서 필요한 항목은 export 로 내보냅니다.

    - 공유기능 모듈에서 제공되는 서비스는 대부분 싱글톤 패턴의 서비스를 작성합니다.

    - 공유기능 모듈에서 공유된 기능은 주로 사용 되는 구성요소, 또는 지시문, 파이프 를 사용합니다.

    - 공유기능 모듈의 서비스를 사용할 시 lazy-loading 으로 외부 모듈들을 불러오는 경우

       providers 에 지정된 서비스를 각자 새로운 인스턴스로 만들어서 사용 될 위험이 있습니다.

    - 각 모듈이 자체적으로 새로운 싱글톤 인스턴스 를 가지지 않게 만들어야 합니다.



    ex) component 를 구성할 때 providers 를 각자 지정 할 수 있습니다.

    @Component({

      selector: 'app-heroes',

      templateUrl: 'heroes.component.html',

      stylesUrls: ['./heroes.component.scss'],

      providers: [heroesService]      <=====  이렇게 해당 구성요소에서 providers 를 지정 합니다. 

    }) 

    - 해당 구성요소에 providers 를 따로 지정 할 경우 해당 구성요소에 접근 할 때 독립된 새로운 서비스 인스턴스를 

       생성하게 됩니다.

    - 만약 SharedModule 에 있는 서비스를 위 예시처럼 작성하거나, SharedModule 을 쓰지 않고, 

       providers 를 사용할 경우 의도치 않은 서비스 인스턴스가 생겨 날 수 있으니 주의해야 합니다.

    - 정상적인 방법으로는 해당 기능의 모듈을 만들고 그 모듈의 providers 에 service 를 주입시키고

       해당 모듈에 속한 컴포넌트에서는 import 로 서비스를 불러와서 사용해야 합니다. 




    Core feature module

    Style 04-11


    * 기능 모듈의 단순화를 위해서 CoreModule 을 만들어 보조, 일회용 클래스를 수집시킵니다.

    * CoreModule 을 만들어 루트 모듈에서 참조하도록 하여 App 의 복잡성을 줄이고, 프로그램의 조정자 역활을

      하게 합니다.

    * core 폴더에 CoreModule 이라는 기능 모듈을 만듭니다. 

      (ex: app / core / core.module.ts )

    * 인스턴스가 해당 프로그램 전체적으로 공유해야하는 싱글톤 서비스를 배치 합니다.

      (ex: ExceptionService, LoggerService 와 같이 전체 프로그램에서 사용되는 서비스 )

    * CoreModule 에서 필요한 모든 모듈을 참조합니다. 

      (ex: FormsModule, CommonModule )

    * 싱글톤 서비스 뿐만 아니라 응용프로그램에서 한번만 불러와서 재사용되는 구성요소를 배치 하십시오.

      (ex: NavComponent, SpinnerComponent )

    * 루트 모듈에서 참조되어 코어 모듈의 모든기능을 사용할 수 있도록 모든 기능을 export 하십시오.

    - SharedModule 이 반복적으로 사용되는 특정 기능을 품고 있다면, CoreModule 은 그 외 

      기능에서 일회용으로 사용 되거나 보조적으로 사용 되는 기능과, 프로그램 전체에서 사용 되지만

      독립적인 구성요소의 기능을 해치지 않는 기능을 탑재 합니다.





    Prevent re-import of the core module 

    Style 04-12


    * Core Module 은 오직 루트 AppModule 에서만 import 해야 합니다.

    - Core Module 을 다시 load 하지 않도록 module-import-guard 를 만들어 방지하십시오.

    - module-guard 를 만들면 core-module 외 싱글톤으로 작성된 모듈의 재로드도 방지됩니다.


    ex)

    app/core/module-import-guard.ts

    export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) { if (parentModule) { throw new Error(`${moduleName} has already been loaded. Import Core modules in the AppModule only.`); } }

    - parentModule 과 moduleName 을 변수로 받는 throwIfAlreadyLoaded 라는 함수를 만들었습니다..

      해당 함수는 이미 불러온 모듈을 다시 불러온다면 error 를 던지게 됩니다.




    app/core/core.module.ts


    1. import { NgModule, Optional, SkipSelf } from '@angular/core';
    2. import { CommonModule } from '@angular/common';
    3.  
    4. import { LoggerService } from './logger.service';
    5. import { NavComponent } from './nav/nav.component';
    6. import { throwIfAlreadyLoaded } from './module-import-guard';
    7.  
    8. @NgModule({
    9. imports: [
    10. CommonModule // we use ngFor
    11. ],
    12.  exports: [NavComponent],
    13.  declarations: [NavComponent],
    14. providers: [LoggerService]
    15. })
    16. export class CoreModule {
    17. constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
    18. throwIfAlreadyLoaded(parentModule, 'CoreModule');
    19. }
    20. }

    - CoreModule 애 새로운 생성자를 추가 했습니다.

    - @Optional 은 선택적으로 종속자를 지정하도록 하는 decorator 입니다. 만약 @Optional 로 지정된 객체가 없다면 null 을 반환합니다.

      (위 코드를 보면 parentModule  = CoreModule 이 없다면 null 을 반환 한다는뜻 = 생성자 내 함수를 실행하지 않습니다.)

    - @SkipSelf 는 부모 인젝터에 종속성을 확인 하도록 합니다. 

       ParentModule 이란 이름의 생성자 변수는 CoreModule 타입이고, 해당 모듈을 부모 인젝터에서 찾게 됩니다.

       CoreModule 은 루트 AppModule 에서 정의 되었기 때문에, 다른 모듈 또는 서비스에서 CoreModule 을 

       불러오려 한다면 부모 인젝터에서 CoreModule 을 찾게되고, 해당 CoreModule 이 존재한다면 위에서 정이한 

       thorwIfAlreadyLoaded 함수를 실행하게 되겠죠. 

    - 만약 부모 인젝터에 CoreModule이 존재하지 않는다면 (지금 경우엔 Application 이 초기 로드 될때) 생성자는 

       @Optional Decorator 에 의해 null 을 반환하기 때문에 thorwIfAlreadyLoaded 함수는 실행되지 않습니다.





    Lazy Loaded folders

    Style 04-13


    * LazyLoad 를 사용하면 application 의 시작지점이 아니라 필요할 때 즉시 불러오도록 할 수 있다.

    - Lazyload 의 기능은 lazyload 폴더에 따로 저장 하십시오.

    (저 같은 경우는 굳이 따로 lazyload 폴더를 만들어 사용하지 않고 application 의 페이지 마다 별도로 모듈을 두고

     lazyload 시키고 있습니다.)



    Never directly import lazy loaded folders

    Style 04-14


    * Lazyload 폴더 (또는 모듈)을 직접적으로 바로 import 하면 안됩니다.

    - 직접적으로 바로 load 해버리면 Lazyload 를 쓰는 의미가 없습니다.





    -----Components


    Component selector names

    Style 05-02


    * Component 의 selector 이름은 대/소문자 방식 또는 '-' 를 사용하십시오.

    - custom elements 의 이름 방식과 일관성을 유지 해야 합니다.

    (저 같은 경우 selector 의 이름은 '-' 를 넣어서 사용합니다.)


    ex) 

    @Component({ selector: 'toh-hero-button', templateUrl: './hero-button.component.html' }) export class HeroButtonComponent {}



    Components as elements

    Style 05-03


    * selector 이름에 template 에 속성으로 지정하듯이  '[]' 를 사용하지 마십시오.

    - Component 의 selector 는 자정된 template 에서 자동으로 속성으로 지정됩니다.


    ex)  잘못된 예

    /* avoid */


    - app/heroes/hero-button/hero-button.component.ts

    @Component({ selector: '[tohHeroButton]', templateUrl: './hero-button.component.html' }) export class HeroButtonComponent {}



    - app/app.component.html

    <!-- avoid --> <div tohHeroButton></div>



    ex) 정상적인 예



    - app/heroes/hero-button/hero-button.component.ts

    @Component({ selector: 'toh-hero-button', templateUrl: './hero-button.component.html' }) export class HeroButtonComponent {}


    - app/app.component.html

    <toh-hero-button></toh-hero-button>





    Extract templates and styles to their own files

    Style 05-04


    * template 과 style 이 3줄을 넘어 갈 경우 별도의 파일로 분리 하십시오.

    * template 파일 의 이름은  [component-name].component.html 로 지정하십시오.

    * style 파일의 이름은 [component-name].component.css 로 지정하십시오.

    * 컴포넌트에서 해당 template 과 style 의 경로에는 상대경로 표시인 './' 를 사용하십시오.

    - template 과 style 코드가 많아질수록 하나의 파일에서 처리하기에는 component 의 용도를 

      모호하게 만들고 가독성이 떨어집니다.

    - 대부분의 IDE 에서는 inline template 에서의 html, style 의 단축키, 부가기능을 사용 할 수 없습니다.

    - './' prefix 를 사용할 경우 구성요소가 함께 이동해도 url 을 변경 할 필요가 없습니다.




    Decorator input and output properties

    Style 05-12


    * Directive 의 Inputs, Outputs 보다 @Input , @Output Decorator 를 사용하십시오.

    - @Input, @Output 의 사용은 가독성을 좋게하고 해당변수를 식별하기 쉽게 합니다.

    - @Input, @Output 과 연결된 이벤트 또는 변수의 이름을 변경할 때 한 곳에서 처리 할 수 있습니다.

    - @Input, @Output Decorator 를 사용하면 코드가 짧아지고 해당 속성들은 Input, Ouput 으로 

      확실하게 식별 할 수 있습니다.

      @Input, @Output Decorator 는 해당 Class 의 상단에 입력하세요.


    ex)  잘못된 예

    1. /* avoid */
    2.  
    3. @Component({
    4. selector: 'toh-hero-button',
    5. template: `<button></button>`,
    6. inputs: [
    7. 'label'
    8. ],
    9. outputs: [
    10. 'change'
    11. ]
    12. })
    13. export class HeroButtonComponent {
    14. change = new EventEmitter<any>();
    15. label: string;


    ex) 수정된 예

    @Component({ selector: 'toh-hero-button', template: `<button>{{label}}</button>` }) export class HeroButtonComponent { @Output() change = new EventEmitter<any>(); @Input() label: string; }


    (전 처음부터 @input @output 을 써서 잘못된 예 적힌 방식은 한번도 안써봤습니다.

     아마 대부분이 그럴 듯)



    Avoid aliasing inputs and outputs

    Style 05-13

     

    * Input 과 Output 의 별칭은 중요하거나 특정 목적을 가지고 있을 때 사용 하십시오.

    - 동일한 속성에 2개의 이름은 혼란스럽게 만듭니다.

    - Direcrive 이름이 입력 등록 정보이고, 해당 Directive 의 이름이 Directive 의 특성을 설명하지 않을 때

      별칭을 사용해야 합니다.


    ex) 잘못된 예


    app/heroes/shared/hero-button/hero-button.component.ts

    /* avoid pointless aliasing */ @Component({ selector: 'toh-hero-button', template: `<button>{{label}}</button>` }) export class HeroButtonComponent { // Pointless aliases @Output('changeEvent') change = new EventEmitter<any>(); @Input('labelAttribute') label: string; }


    app/app.component.html

    <!-- avoid --> <toh-hero-button labelAttribute="OK" (changeEvent)="doSomething()"> </toh-hero-button>


    - 굳이 별칭을 사용할 필요성을 느끼지 못 합니다.

      label 이라는 이름에 이미 label 이라는 특징이 포함되었고, changeEvent 같은 경우도 마찬가지 입니다.



    ex) 수정된 예


    app/heroes/shared/hero-button/hero-button.component.ts

    @Component({ selector: 'toh-hero-button', template: `<button>{{label}}</button>` }) export class HeroButtonComponent { // No aliases @Output() change = new EventEmitter<any>(); @Input() label: string; }


    app/heroes/shared/hero-button/hero-highlight.directive/ts

    1. import { Directive, ElementRef, Input, OnChanges } from '@angular/core';
    2.  
    3. @Directive({ selector: '[heroHighlight]' })
    4. export class HeroHighlightDirective implements OnChanges {
    5.  
    6. // Aliased because `color` is a better property name than `heroHighlight`
    7. @Input('heroHighlight') color: string;
    8.  
    9. constructor(private el: ElementRef) {}
    10.  
    11. ngOnChanges() {
    12. this.el.nativeElement.style.backgroundColor = this.color || 'yellow';
    13. }
    14. }



    app/app.component.html

    <toh-hero-button label="OK" (change)="doSomething()"> </toh-hero-button> <!-- `heroHighlight` is both the directive name and the data-bound aliased property name --> <h3 heroHighlight="skyblue">The Great Bombasto</h3>


    - Output, Input 의 별칭을 사용하지 않습니다.

    - Directive 같은 경우 heroHighlight 라는 별칭을 사용하였고 해당 지시자의 용도는 배경 색상을 

       바꾸는 용도 입니다. 일반적인 html 코딩에는 heroHighlight 라는 이름으로 일반적인 속성이 아닌 

       특징이 있는 지시자라는 것을 알 수 있고, 지시자 내부 에서는 color 라는 이름으로 해당 input 이 

       색상을 뜻하는 것을 알 수 있습니다.  (이름이 color 보다는 bgColor 가 더 괜찮아 보입니다.)

       그와 더불어 별칭을 주는 또 하나의 이유는  color 또는 bgColor 는 다른 요소에서도 자주 사용되는 

       요소이기 때문에  별칭을 주는 것이 적합해 보이는 또 하나의 이유입니다.




    Member sequence

    Style 05-14


    * 변수와 속성을 위에 두고 함수는 밑에 두십시오.

    * 각 변수와 속성, 함수 는 알파벳 순서로 두십시오.

    - 변수와 속성,함수 를 일관된 순서로 배치하면 읽기 쉽고, 식별 하기 쉽습니다.


    ex)  잘못된 예

    1. /* avoid */
    2.  
    3. export class ToastComponent implements OnInit {
    4.  
    5. private defaults = {
    6. title: '',
    7. message: 'May the Force be with you'
    8. };
    9. message: string;
    10. title: string;
    11. private toastElement: any;
    12.  
    13. ngOnInit() {
    14. this.toastElement = document.getElementById('toh-toast');
    15. }
    16.  
    17. // private methods
    18. private hide() {
    19. this.toastElement.style.opacity = 0;
    20. window.setTimeout(() => this.toastElement.style.zIndex = 0, 400);
    21. }
    22.  
    23. activate(message = this.defaults.message, title = this.defaults.title) {
    24. this.title = title;
    25. this.message = message;
    26. this.show();
    27. }
    28.  
    29. private show() {
    30. console.log(this.message);
    31. this.toastElement.style.opacity = 1;
    32. this.toastElement.style.zIndex = 9999;
    33.  
    34. window.setTimeout(() => this.hide(), 2500);
    35. }
    36. }

    - 뒤죽박죽 되어있는 속성과 함수 


    ex) 수정된 예

    1. export class ToastComponent implements OnInit {
    2. // public properties
    3. message: string;
    4. title: string;
    5.  
    6. // private fields
    7. private defaults = {
    8. title: '',
    9. message: 'May the Force be with you'
    10. };
    11. private toastElement: any;
    12.  
    13. // public methods
    14. activate(message = this.defaults.message, title = this.defaults.title) {
    15. this.title = title;
    16. this.message = message;
    17. this.show();
    18. }
    19.  
    20. ngOnInit() {
    21. this.toastElement = document.getElementById('toh-toast');
    22. }
    23.  
    24. // private methods
    25. private hide() {
    26. this.toastElement.style.opacity = 0;
    27. window.setTimeout(() => this.toastElement.style.zIndex = 0, 400);
    28. }
    29.  
    30. private show() {
    31. console.log(this.message);
    32. this.toastElement.style.opacity = 1;
    33. this.toastElement.style.zIndex = 9999;
    34. window.setTimeout(() => this.hide(), 2500);
    35. }
    36. }

    - public 속성 - private 속성 - public 함수 - private 함수 순으로 배치 




    Delegate complex component logic to services

    Style 05-15


    * component 의 logic 은 해당 component 에서 필요한 logic 으로만 제한 해야 합니다.

    * 재사용 가능한 method, logic 은 모두 service 로 위임되어야 합니다.

    - logic 은 서비스내에 배치되고 함수로 노출될 때 여러 구성요소 에서 재사용 될 수 있습니다.

    - 단위 테스트를 시행할 때 서비스 로직을 쉽게 분리할 수 있고, component 에서 서비스 함수를 호출 하는 

       logic 을 쉽게 알 수 있습니다.

    - 종속성 을 제거하고, component 의 세부 사항을 숨길 수 있습니다.

    - component 를 작게 만들고, 구성요소에 집중 할 수 있게 해줍니다.


    ex) 잘못된 예

    1. /* avoid */
    2.  
    3. import { OnInit } from '@angular/core';
    4. import { Http, Response } from '@angular/http';
    5.  
    6. import { Observable } from 'rxjs/Observable';
    7. import 'rxjs/add/operator/catch';
    8. import 'rxjs/add/operator/finally';
    9. import 'rxjs/add/operator/map';
    10.  
    11. import { Hero } from '../shared/hero.model';
    12.  
    13. const heroesUrl = 'http://angular.io';
    14.  
    15. export class HeroListComponent implements OnInit {
    16. heroes: Hero[];
    17. constructor(private http: Http) {}
    18. getHeroes() {
    19. this.heroes = [];
    20. this.http.get(heroesUrl)
    21. .map((response: Response) => <Hero[]>response.json().data)
    22. .catch(this.catchBadResponse)
    23. .finally(() => this.hideSpinner())
    24. .subscribe((heroes: Hero[]) => this.heroes = heroes);
    25. }
    26. ngOnInit() {
    27. this.getHeroes();
    28. }
    29.  
    30. private catchBadResponse(err: any, source: Observable<any>) {
    31. // log and handle the exception
    32. return new Observable();
    33. }
    34.  
    35. private hideSpinner() {
    36. // hide the spinner
    37. }
    38. }

    - component 에 http 와 관련된 메소드가 있습니다. 해당 메소드는 영웅의 목록을 불러오는 메소드로서

      해당 component 외에 다른 component 에서도 사용 될 수 있는 메소드 입니다.



    ex) 수정된 예

    1. import { Component, OnInit } from '@angular/core';
    2.  
    3. import { Hero, HeroService } from '../shared';
    4.  
    5. @Component({
    6. selector: 'toh-hero-list',
    7. template: `...`
    8. })
    9. export class HeroListComponent implements OnInit {
    10. heroes: Hero[];
    11. constructor(private heroService: HeroService) {}
    12. getHeroes() {
    13. this.heroes = [];
    14. this.heroService.getHeroes()
    15. .subscribe(heroes => this.heroes = heroes);
    16. }
    17. ngOnInit() {
    18. this.getHeroes();
    19. }
    20. }

    -  해당 getHeroes 메소드는 heroService 로 위임하였습니다.




    Don't prefix output properties

    Style 05-16


    * Output 에 접두사를 사용하지 마십시오.

    * Output 이 아닌 Output 을 처리하는 메소드에 On 접두사를 붙이십시오.

    - eventEmitter 로 보내진 데이터를 처리하는 하는 것은 버튼을 클릭하는 것과 같습니다.

    - Angular 는 on-* 에 대한 대체 구문을 지원합니다.  on-  을 붙일 경우 on-onEvent 방식이 될 수 있습니다.


    ex) 대체 구문

      (target)="statement"

    on-target="statement"

    (이런식으로 on- 이 이미 쓰이기 때문에 on 을 붙이는 것 은 좋지 않습니다.)



    ex) 잘못된 예

    app/heroes/hero.component.ts

    /* avoid */ @Component({ selector: 'toh-hero', template: `...` }) export class HeroComponent { @Output() onSavedTheDay = new EventEmitter<boolean>(); }


    app/app,component.html

    <!-- avoid --> <toh-hero (onSavedTheDay)="onSavedTheDay($event)"></toh-hero>


    - Output 과 이벤트 처리기 양쪽에 on 접두사가 붙어 있습니다.



    ex) 수정된 예

    app/heroes/hero.component.ts

    export class HeroComponent { @Output() savedTheDay = new EventEmitter<boolean>(); }


    app/app.component.html

    <toh-hero (savedTheDay)="onSavedTheDay($event)"></toh-hero>


    - output 에 접두사를 제거 했습니다.





    Put presentation logic in the component class

    Style 05-17


    * 프리젠테이션 로직을 component 에 작성하십시오.

    - template 이 아닌 component 에 로직이 집중되어야 합니다.

    - component 의 프리젠테이션 로직을 template 이 아닌 class 에 유지하면 유지, 관리, 재사용 성이 

       향상됩니다.


    ex) 잘못된 예

    1. /* avoid */
    2.  
    3. @Component({
    4. selector: 'toh-hero-list',
    5. template: `
    6. <section>
    7. Our list of heroes:
    8. <hero-profile *ngFor="let hero of heroes" [hero]="hero">
    9. </hero-profile>
    10. Total powers: {{totalPowers}}<br>
    11. Average power: {{totalPowers / heroes.length}}
    12. </section>
    13. `
    14. })
    15. export class HeroListComponent {
    16. heroes: Hero[];
    17. totalPowers: number;
    18. }

    - template 의 Average power 에  {{ totalPowers / heroes.lengrh }} 라는 로직 이 들어갔습니다.



    ex) 수정된 예

    1. @Component({
    2. selector: 'toh-hero-list',
    3. template: `
    4. <section>
    5. Our list of heroes:
    6. <toh-hero *ngFor="let hero of heroes" [hero]="hero">
    7. </toh-hero>
    8. Total powers: {{totalPowers}}<br>
    9. Average power: {{avgPower}}
    10. </section>
    11. `
    12. })
    13. export class HeroListComponent {
    14. heroes: Hero[];
    15. totalPowers: number;
    16.  
    17. get avgPower() {
    18. return this.totalPowers / this.heroes.length;
    19. }
    20. }

    - {{ totalPowers / heroes.lengrh }} 로직을 class 에 get avgPower() 로 만들었습니다.




    ----- Directives



    Use Directives to enhance an element

    Style 06-01


    * Template 이 없는 프리젠테이션 로직을 작성 할 때는 Directive 를 사용 하십시오.

    - Directive 에는 template 이 없습니다.

    - element 에는 한개 이상의 지시분이 사용될 수 있습니다.


    ex)

    app/shared/highlight.directive.ts

    @Directive({ selector: '[tohHighlight]' }) export class HighlightDirective { @HostListener('mouseover') onMouseEnter() { // do highlight work } }


    app/app.component.html

    <div tohHighlight>Bombasta</div>




    HostListener / HostBinding decorators versus host metadata

    Style 06-03


    * @HostListener / @HostBinding 또는 host metadata 방식을 사용시 일관성을 유지 하십시오.

    - Directive 와 componenet 의 Decorator 에서 @HostListener and @HostBinding 속성을 

       Directive 와 Component 에 지정합니다.

    - @HostBinding 과 @HostListener 연결된 속성 또는 메소드는 클래스의 단일 위치에서만 

       수정이 가능합니다. (해당 Decorator 를 지정한 곳에서만 수정된다는 거죠.)

    - 하지만 host metadata 를 사용할 경우 지정해놓은 등록정보를 제어기 내부 어디서든 

      수정이 가능합니다. 하지만 변경 시 해당 지정문과 연관된 메타 데이터를 모두 수정해야 합니다.


    ex) @HostListener, @HostBinding  사용 시

    import { Directive, HostBinding, HostListener } from '@angular/core'; @Directive({ selector: '[tohValidator]' }) export class ValidatorDirective { @HostBinding('attr.role') role = 'button'; @HostListener('mouseenter') onMouseEnter() { // do work } }



    ex) Host metadata 사용 시

    1. import { Directive } from '@angular/core';
    2.  
    3. @Directive({
    4. selector: '[tohValidator2]',
    5. host: {
    6. '[attr.role]': 'role',
    7. '(mouseenter)': 'onMouseEnter()'
    8. }
    9. })
    10. export class Validator2Directive {
    11. role = 'button';
    12. onMouseEnter() {
    13. // do work
    14. }
    15. }


    - metadata 방식과 decorator 의 차이점이 보이십니까?

      decorator 방식은 import 가 필요하고 해당 속성을 정의한 곳에서만 수정이 가능합니다.

      metadata 방식은 추가  import 가 필요하지 않고 연결시킨 속성을 어디서든 변경이 가능합니다.

      다만 변경이 있을 시 속성전체를 찾아 다시 변경해야 합니다.

      어떤 것을 선택하든 개발자가 원하는 방식을 선택하십시오.




    ----- Services


    Services are singletons

    Style 07-01


    * 동일한 인젝터 에서는  싱글톤으로 서비스를 사용 하여 데이터 및 기능 공유에 사용하십시오.

    - 서비스는 기능의 영역과 메소드를 공유하는데 제격입니다.

    - 서비스는 상태저장 메모리 안 데이터 공유에 이상적 입니다.


    ex)

    export class HeroService { constructor(private http: Http) { } getHeroes() { return this.http.get('api/heroes') .map((response: Response) => <Hero[]>response.json().data); } }

    - 일반적인 서비스 방식입니다.




    Single responsibility

    Style 07-02


    * 캡슐화 된 컨텍스트 로 구성된 서비스를 만드십시오.

    * 서비스가 해당 목적을 초과하여 사용 될 경우 새로운 서비스를 만드십시오.

    - 하나의 서비스에 많은 기능이 있는 경우 테스트가 어려워집니다.

    - 하나의 서비스에 많은 기능이 있는 경우 해당 서비스를 주입 받는 구성요소도 

      무거워 집니다.




    Providing a service

    Style 07-03


    * 가장 최상위 부모 구성요소에 Service 를 provide 하십시오.

    - Angular 의 인젝터는 계층적으로 이뤄져 있습니다.

    - 최상위 부모 구성요소에 서비스를 제공하면 인스턴스가 공유되어 해당 최상위 구성요소의 

       모든 하위 구성요소에서 사용이 가능합니다.

    - 이런 상태는 서비스가 메소드나 상태를 공유할 때 이상적입니다.

    - 만약 두개의 서로 다른 component 가 서로 다른 서비스를 필요로 한다면 이것은 이상적이지 않습니다.

       이럴 때는 새로운 인스턴스를 필요한 구성요소에 제공해야 합니다.


    ex)

    app/app.component.ts

    1. import { Component } from '@angular/core';
    2.  
    3. import { HeroService } from './heroes';
    4.  
    5. @Component({
    6. selector: 'toh-app',
    7. template: `
    8. <toh-heroes></toh-heroes>
    9. `,
    10. providers: [HeroService]
    11. })
    12. export class AppComponent {}



    app/heroes/hero-list/hero-list.component.ts

    1. import { Component, OnInit } from '@angular/core';
    2.  
    3. import { Hero, HeroService } from '../shared';
    4.  
    5. @Component({
    6. selector: 'toh-heroes',
    7. template: `
    8. <pre>{{heroes | json}}</pre>
    9. `
    10. })
    11. export class HeroListComponent implements OnInit {
    12. heroes: Hero[] = [];
    13.  
    14. constructor(private heroService: HeroService) { }
    15.  
    16. ngOnInit() {
    17. this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);
    18. }
    19. }

    - 상위 component 인 app.component 에 heroservice 를 provide 하고 하위 component 에서 

      provide 없이 heroservice 를 사용한다.

     ( 전 개인적으로 모든 구성요소 모음에 모듈을 만들기 때문에 component 에 직접적으로 provide 하지 않고 

      module 별로 provide 합니다.)




    Use the @Injectable() class decorator

    Style 07-04


    * 타입을 서비스의 의존성으로 사용할 때 @Inject 보다는 @Injectable Decorator 를 사용 하십시오.

    - Angular 의 의존성 주입 메커니즘은 서비스의 생성자 매개변수에 선언 된 유형을 기반으로 서비스의 종속성을 해결합니다.

    - 각 개별 매개변수에 @Inject() 를 사용하는 것 보다 @Injectable 구문 사용이 훨씬 간략합니다.


    ex) 잘못된 예

    /* avoid */ export class HeroArena { constructor( @Inject(HeroService) private heroService: HeroService, @Inject(Http) private http: Http) {} }



    ex) 수정된 예

    @Injectable() export class HeroArena { constructor( private heroService: HeroService, private http: Http) {} }



    ----- Data Services



    Talk to the server through a service

    Style 08-01


    * 데이터 조작 및 데이터 서비스와의 데이터 상호 작용을 위해 리팩토링 을 하십시오.

    * 데이터 서비스는 XHR 호출, 로컬 스토리지 , 메모리 스태싱, 또는 기타 데이터 조작을 담당하게 합니다.

    - component 에서 책임져야 될 것은 View 정보의 표현과 수집 입니다. 데이터를 어떻게 얻는지는 Component 에서 알 필요가 없습니다.

    - 데이터 서비스를 분리하면, component 의 구성이 더 간단해지고 로직이 집중 될 수 있습니다.

    - 데이터 서비스를 분리하면 test 를 더욱 쉽게 할 수 있습니다.

    - 데이터 서비스는 http, header, caching 등 componet 가 필요치 않는 로직을 담당하여 component 의 test 가 손쉽게 이뤄지도록

      돕습니다.




    ----- Lifecycle hooks 

    - Lifecycle hooks 을 사용하여 angular 에서 작성된 이벤트를 활용하십시오.



    Implement lifecycle hook interfaces

    Style 09-01


    * lifecycle hook interfaces 를 구현하십시오.

    - lifecycle hook interfaces 는 유형이 지정된 메소드를 처리합니다. 

       이러한 interface 를 사용하여 철자 및 구문 오류를 표시하십시오.


    ex) 잘못된 예

    /* avoid */ @Component({ selector: 'toh-hero-button', template: `<button>OK<button>` }) export class HeroButtonComponent { onInit() { // misspelled console.log('The component is initialized'); } }



    ex) 수정된 예

    @Component({ selector: 'toh-hero-button', template: `<button>OK</button>` }) export class HeroButtonComponent implements OnInit { ngOnInit() { console.log('The component is initialized'); } }



    ----- Appendix

    - angular 를 사용할때 유용한 도구와 팁.



    Codelyzer

    Style A-01


    * Angular 의 lint 처리와 코드의 잘못된 부분을 Angular 에 맞게 표시 해 줍니다.

    - npm 으로 install 해서 사용하십시오. 자세한건 Codelyzer 을 검색하십시오.




    File templates and snippets

    Style A-02


    * 파일 템플리트 또는 스니펫을 사용하여 일관된 스타일 및 패턴을 사용하십시오.

    - Angular TypeScript Snippets 를 VsCode, Atom, SublimeText, Vim 에서 사용하십시오.

    Use Extension





    (끝)

    - 예전보다 가이드가 조금씩 변경된 것 같네요.

    - 혹여 잘못된 점 있으면 댓글 달아 주십시오. 수정 하겠습니다.






    댓글