-
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 Name File 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 Name File 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 Name File 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 Type File 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 Type File 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 Name File 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>-------------------------------------------------------------------------------------------------------------------------------------------------
- 큰 주제의 폴더를 만들고 해당 폴더 내에 기능별 폴더를 정리합니다.
- 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
- import { NgModule, Optional, SkipSelf } from '@angular/core';
- import { CommonModule } from '@angular/common';
- import { LoggerService } from './logger.service';
- import { NavComponent } from './nav/nav.component';
- import { throwIfAlreadyLoaded } from './module-import-guard';
- @NgModule({
- imports: [
- CommonModule // we use ngFor
- ],
- exports: [NavComponent],
- declarations: [NavComponent],
- providers: [LoggerService]
- })
- export class CoreModule {
- constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
- throwIfAlreadyLoaded(parentModule, 'CoreModule');
- }
- }
- 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) 잘못된 예
- /* avoid */
- @Component({
- selector: 'toh-hero-button',
- template: `<button></button>`,
- inputs: [
- 'label'
- ],
- outputs: [
- 'change'
- ]
- })
- export class HeroButtonComponent {
- change = new EventEmitter<any>();
- 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
- import { Directive, ElementRef, Input, OnChanges } from '@angular/core';
- @Directive({ selector: '[heroHighlight]' })
- export class HeroHighlightDirective implements OnChanges {
- // Aliased because `color` is a better property name than `heroHighlight`
- @Input('heroHighlight') color: string;
- constructor(private el: ElementRef) {}
- ngOnChanges() {
- this.el.nativeElement.style.backgroundColor = this.color || 'yellow';
- }
- }
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) 잘못된 예
- /* avoid */
- export class ToastComponent implements OnInit {
- private defaults = {
- title: '',
- message: 'May the Force be with you'
- };
- message: string;
- title: string;
- private toastElement: any;
- ngOnInit() {
- this.toastElement = document.getElementById('toh-toast');
- }
- // private methods
- private hide() {
- this.toastElement.style.opacity = 0;
- window.setTimeout(() => this.toastElement.style.zIndex = 0, 400);
- }
- activate(message = this.defaults.message, title = this.defaults.title) {
- this.title = title;
- this.message = message;
- this.show();
- }
- private show() {
- console.log(this.message);
- this.toastElement.style.opacity = 1;
- this.toastElement.style.zIndex = 9999;
- window.setTimeout(() => this.hide(), 2500);
- }
- }
- 뒤죽박죽 되어있는 속성과 함수
ex) 수정된 예
- export class ToastComponent implements OnInit {
- // public properties
- message: string;
- title: string;
- // private fields
- private defaults = {
- title: '',
- message: 'May the Force be with you'
- };
- private toastElement: any;
- // public methods
- activate(message = this.defaults.message, title = this.defaults.title) {
- this.title = title;
- this.message = message;
- this.show();
- }
- ngOnInit() {
- this.toastElement = document.getElementById('toh-toast');
- }
- // private methods
- private hide() {
- this.toastElement.style.opacity = 0;
- window.setTimeout(() => this.toastElement.style.zIndex = 0, 400);
- }
- private show() {
- console.log(this.message);
- this.toastElement.style.opacity = 1;
- this.toastElement.style.zIndex = 9999;
- window.setTimeout(() => this.hide(), 2500);
- }
- }
- 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) 잘못된 예
- /* avoid */
- import { OnInit } from '@angular/core';
- import { Http, Response } from '@angular/http';
- import { Observable } from 'rxjs/Observable';
- import 'rxjs/add/operator/catch';
- import 'rxjs/add/operator/finally';
- import 'rxjs/add/operator/map';
- import { Hero } from '../shared/hero.model';
- const heroesUrl = 'http://angular.io';
- export class HeroListComponent implements OnInit {
- heroes: Hero[];
- constructor(private http: Http) {}
- getHeroes() {
- this.heroes = [];
- this.http.get(heroesUrl)
- .map((response: Response) => <Hero[]>response.json().data)
- .catch(this.catchBadResponse)
- .finally(() => this.hideSpinner())
- .subscribe((heroes: Hero[]) => this.heroes = heroes);
- }
- ngOnInit() {
- this.getHeroes();
- }
- private catchBadResponse(err: any, source: Observable<any>) {
- // log and handle the exception
- return new Observable();
- }
- private hideSpinner() {
- // hide the spinner
- }
- }
- component 에 http 와 관련된 메소드가 있습니다. 해당 메소드는 영웅의 목록을 불러오는 메소드로서
해당 component 외에 다른 component 에서도 사용 될 수 있는 메소드 입니다.
ex) 수정된 예
- import { Component, OnInit } from '@angular/core';
- import { Hero, HeroService } from '../shared';
- @Component({
- selector: 'toh-hero-list',
- template: `...`
- })
- export class HeroListComponent implements OnInit {
- heroes: Hero[];
- constructor(private heroService: HeroService) {}
- getHeroes() {
- this.heroes = [];
- this.heroService.getHeroes()
- .subscribe(heroes => this.heroes = heroes);
- }
- ngOnInit() {
- this.getHeroes();
- }
- }
- 해당 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) 잘못된 예
- /* avoid */
- @Component({
- selector: 'toh-hero-list',
- template: `
- <section>
- Our list of heroes:
- <hero-profile *ngFor="let hero of heroes" [hero]="hero">
- </hero-profile>
- Total powers: {{totalPowers}}<br>
- Average power: {{totalPowers / heroes.length}}
- </section>
- `
- })
- export class HeroListComponent {
- heroes: Hero[];
- totalPowers: number;
- }
- template 의 Average power 에 {{ totalPowers / heroes.lengrh }} 라는 로직 이 들어갔습니다.
ex) 수정된 예
- @Component({
- selector: 'toh-hero-list',
- template: `
- <section>
- Our list of heroes:
- <toh-hero *ngFor="let hero of heroes" [hero]="hero">
- </toh-hero>
- Total powers: {{totalPowers}}<br>
- Average power: {{avgPower}}
- </section>
- `
- })
- export class HeroListComponent {
- heroes: Hero[];
- totalPowers: number;
- get avgPower() {
- return this.totalPowers / this.heroes.length;
- }
- }
- {{ 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 사용 시
- import { Directive } from '@angular/core';
- @Directive({
- selector: '[tohValidator2]',
- host: {
- '[attr.role]': 'role',
- '(mouseenter)': 'onMouseEnter()'
- }
- })
- export class Validator2Directive {
- role = 'button';
- onMouseEnter() {
- // do work
- }
- }
- 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
- import { Component } from '@angular/core';
- import { HeroService } from './heroes';
- @Component({
- selector: 'toh-app',
- template: `
- <toh-heroes></toh-heroes>
- `,
- providers: [HeroService]
- })
- export class AppComponent {}
app/heroes/hero-list/hero-list.component.ts
- import { Component, OnInit } from '@angular/core';
- import { Hero, HeroService } from '../shared';
- @Component({
- selector: 'toh-heroes',
- template: `
- <pre>{{heroes | json}}</pre>
- `
- })
- export class HeroListComponent implements OnInit {
- heroes: Hero[] = [];
- constructor(private heroService: HeroService) { }
- ngOnInit() {
- this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);
- }
- }
- 상위 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 에서 사용하십시오.
(끝)
- 예전보다 가이드가 조금씩 변경된 것 같네요.
- 혹여 잘못된 점 있으면 댓글 달아 주십시오. 수정 하겠습니다.
'Angular' 카테고리의 다른 글
새로운 Angular engine, Ivy Compiler 에 대하여 알아봅시다. (0) 2019.11.04 Angular Http Interceptor 사용하기 (2) 2019.07.26 Angular 4 업데이트 변경점 정리 (1) 2017.03.26 9장) Angular 2 -HeroEditor- (5-2) (2) 2017.02.04 8장) Angular 2 -HeroEditor- (5-1) (2) 2017.01.14