CHAPTER 9. Building components and directives
Introduction
Component๋ ์ง์๋ฌธ๊ณผ ์ค์ ๋ก ๋ค๋ฅด์ง ์๋ค. ๋จ์ง ๋ ๊ฐ์ ์ ํ์ ์์ฑ์ด ์์ผ๋ฉฐ ๊ด๋ จ ๋ทฐ๊ฐ ์์ด์ผ ํ๋ค. ์ง์์ด์ ๋น๊ตํ์ฌ ๋ง์ ์๋ก์ด ์์ฑ์ ๊ฐ์ ธ ์ค์ง๋ ์๋๋ค.
Directives
์ง์๋ฌธ์ Template์ด ์๋ ๊ฒ์ ์ ์ธํ๊ณ ๋ ๊ตฌ์ฑ ์์์ ๋งค์ฐ ๋น์ทํ๋ค. ์ฌ์ค, Component ํด๋์ค๋ ํ๋ ์ ์ํฌ์์ Directive ํด๋์ค๋ฅผ ์์ํ๋ค. ์ง์์ด ๋ถ์์ ๋จผ์ ํด๋ณด๋ ๊ฒ์ด ์ข๋ค. ์ง์๋ฌธ๊ณผ ๊ด๋ จํ์ฌ ์ฐ๋ฆฌ๊ฐ ๋ณผ ์์๋ ๋ชจ๋ ๊ฒ์ด ๊ตฌ์ฑ ์์์๋ ์ ์ฉ๋๋ค. ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ๊ตฌ์ฑ ์ต์ ์ ์ดํด ๋ณด๋๋ก ํ์. ๋ ๊ณ ๊ธ ์ต์ ์ ์ถํ์ ๊ธฐ๋ณธ ๋ง์คํฐ ๋ ์ค๋น๊ฐ๋์ด ์๋ค.
๊ตฌ์ฑ ์์์ ๊ฒฝ์ฐ ์ง์๋ฌธ์ ์ฅ์์๋ฅผ ์ถ๊ฐํ์ง๋ง @Component ๋์ @Directive๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
์ง์์ด๋ ๋งค์ฐ ์์ ์์ญ์ด๋ฉฐ HTML์ ๋ฐ์ฝ๋ ์ดํฐ๋ผ๊ณ ์๊ฐํ ์ ์๋ค. DOM์ ์์์ behavior๋ฅผ ์ฒจ๋ถํ๋ฉด ๋๋ค. ๋์ผํ ์์์ ์ฌ๋ฌ ๊ฐ์ ์ง์๋ฌธ์ ์ฌ์ฉํ ์ ์๋ค.
์ง์์ด์๋ ํ ํ๋ฆฟ์์ ํ์ฑํ ํ ์์น๋ฅผ ํ๋ ์ ์ํฌ์ ์๋ ค์ฃผ๋ CSS ์ ํ์๊ฐ ์์ด์ผ ํ๋ค.
Selectors
์ ๋ ํฐ๋ ๋ค์ํ ์ ํ์ด ๋ ์ ์๋ค. โข ์์ : ๋๊ฐ ๊ตฌ์ฑ ์์์ ๊ฒฝ์ฐ์ฒ๋ผ ๋ฐ๋ฅ ๊ธ : footer. โข ๋น๋ฒํ์ง๋ ์์ class.: .alert โข ์ง์์ด์ ๊ฐ์ฅ ์์ฃผ ์ฌ์ฉ๋๋ ์์ฑ : [color]. โข ํน์ ๊ฐ์ ๊ฐ๋ ์์ฑ : [color = red]. โข ์์ ์กฐํฉ : footer [color = red]๋ ๊ฐ์ด ๋นจ๊ฐ์ ์ธ ์์ฑ ์์ ๊ฐ์ง footer ๋ผ๋ ์์์ ์ผ์นํ๋ค. [color], footer.alert๋ color ์์ฑ์ ๊ฐ์ง ๋ชจ๋ ์์ ๋๋ footer๋ผ๋ ์์๋ฅผ CSS ํด๋์ค alert๊ณผ ์ผ์น ์ํจ๋ค. footer : not (.alert)๋ CSS ํด๋์ค ๊ฒฝ๊ณ ๊ฐ ์๋ (: not ()) footer๋ผ๋ ์์๋ฅผ ์ฐพ๋๋ค.
์๋ฅผ ๋ค์ด, ์ด๊ฒ์ ์๋ฌด๊ฒ๋ํ์ง ์๊ณ ์์ฑ doNothing์ด ์์์์์ ๊ฒฝ์ฐ ํ์ฑํ๋๋ ๋งค์ฐ ๊ฐ๋จํ ์ง์์ด ์ด๋ค.
@Directive({
selector: '[doNothing]'
})
export class DoNothingDirective {
constructor() {
console.log('Do nothing directive');
}
}
๊ทธ๋ฌํ ์ง์์๋ ์ด TestComponent์ ๊ฐ์ ์ปดํฌ๋ํธ์์ ํ์ฑํ ๋ ๊ฒ์ด๋ค.
@Component({
selector: 'ns-test',
template: '<div doNothing>Click me</div>'
})
export class TestComponent {
}
๋ ๋ณต์ํฉ ์ ํ์๊ฐ ๋ ์ ์๋ค.
@Directive({
selector: 'div.loggable[logText]:not([notLoggable=true])'
})
export class ComplexSelectorDirective {
constructor() {
console.log('Complex selector directive');
}
}
์ฌ๊ธฐ์๋ ๋ชจ๋ div ์์๋ฅผ loggable ํด๋์ค์ ์ผ์น ์ํค๋ฉฐ logText ์์ฑ์ true ๊ฐ์ ๊ฐ์ง notLoggable ์์ฑ์ ๊ฐ์ง ์๋๋ค.
<div class="loggable" logText="text">Hello</div>
๊ทธ๋ฌ๋ ์ด๊ฒ์ ํ ๊ฐ์ง๊ฐ ์๋๊ฒ์ด๋ค.
<div class="loggable" logText="text" notLoggable="true">Hello</div>
์์งํ๊ฒ ๋ง์ฝ ๋น์ ์ด ์ด๋ฐ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ๋ ค๊ณ ํ๋ค๋ฉด ์๋ชป ์ฌ์ฉ๋ ๊ฒ์ด๋ค.
์์, ํ์ , ids, ์์ผ๋ ์นด๋ ๋ฐ ์์ฌ (์ : not ์ด์ธ)์ ๊ฐ์ CSS ์ ํ๊ธฐ๋ ์ง์๋์ง ์์ต๋๋ค. ์์ฑ ์ ํ์๋ฅผ bind-, on-, let- ๋๋ ref-๋ก ์์ํ๋ฉด ์๋๋ค. : ํ์์ ๋ฐ๋ผ์ ๋ค๋ฅธ ์๋ฏธ๋ฅผ ๊ฐ์ง๋ค. ์๋ํ๋ฉด ํ์ค ํ ํ๋ฆฟ ๊ตฌ๋ฌธ์ ์ผ๋ถ ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
Inputs
๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ผ๋ฐ์ ์ผ๋ก ๊ตฌ์ฑ ์์ ๋๋ ์ง์๋ฌธ์ ๋ง๋๋ ์์ ์ ํฐ ๋ถ๋ถ์ผ ๊ฒ์ด๋ค. ์์ ๊ตฌ์ฑ ์์์์ ํ์ ์์ ์ค ํ๋์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ๋๋ง๋ค ์์ฑ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ๋ค.
์ด๋ฅผ ์ํด @Directive ๋ฐ์ฝ๋ ์ดํฐ์ inputs ์์ฑ์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ํ์ฉํ๋ ๋ชจ๋ ์์ฑ์ ์ ์ํ๋ค. ์ด ์์ฑ์ ๊ฐ๊ฐ์ property : binding ์์ ๋ฌธ์์ด ๋ฐฐ์ด์ ๋ฐ์ ๋ค์ธ๋ค. property๋ ์ง์์ด ์ธ์คํด์ค ์์ฑ์ ๋ํ๋ด๊ณ binding์ ํํ์์ ํฌํจ ํ DOM ์์ฑ์ด๋ค.
์๋ฅผ ๋ค์ด ์ด ์ง์๋ฌธ์ DOM ์์ฑ logText๋ฅผ ์ง์๋ฌธ ์ธ์คํด์ค ์์ฑ ํ ์คํธ์ ๋ฐ์ธ๋ฉํ๋ค.
@Directive({
selector: '[loggable]',
inputs: ['text: logText']
})
export class SimpleTextDirective {
}
๋น์ ์ ์ง์์ด์ ์์ฑ์ด ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ๋ ๊ทธ๊ฒ์ด ์์ฑ๋๋ค. ๋ค์์ ์ ๋ ฅ์ด ๋ณ๊ฒฝ ๋ ๋๋ง๋ค ์์ฑ์ด ์๋์ผ๋ก ์ ๋ฐ์ดํธ ๋๋ค.
<div loggable logText="Some text">Hello</div>
์์ฑ์ด ๋ณ๊ฒฝ ๋ ๋ ์๋ฆผ์ ๋ฐ์ผ๋ ค๋ฉด ์ง์นจ์ setter ๋ฅผ ์ถ๊ฐ ํ ์ ์๋ค. setter๋ logText ์์ฑ์ด ๋ณ๊ฒฝ ๋ ๋๋ง๋ค ํธ์ถ๋๋ค.
@Directive({
selector: '[loggable]',
inputs: ['text: logText']
})
export class SimpleTextWithSetterDirective {
set text(value) {
console.log(value);
}
}
๋ง์ฝ ๋น์ ์ด ์ฌ์ฉํ๋ ค๊ณ ํ๋ค๋ฉด
<div loggable logText="Some text">Hello</div>
// our directive will log "Some text"
๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ๋ณผ ์ ์๋ค. ์ฌ๊ธฐ์ ํ ์คํธ๋ ์ ์ ์ด์ง๋ง ๋ฌผ๋ก ๋ณด๊ฐ๋ฒ์ ์ฌ์ฉํ์ฌ ์ฝ๊ฒ ๋์ ์ธ ๊ฐ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
<div loggable logText="{{expression}}">Hello</div>
// our directive will log the value of 'expression' in the component
๋๋ ๋๊ดํธ ๊ตฌ๋ฌธ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
<div loggable [logText]="expression">Hello</div>
// our directive will log the value of 'expression' in the component
์ด๊ฒ์ ์๋ก์ด ํ ํ๋ฆฟ ๊ตฌ๋ฌธ์ ๊ฐ์ฅ ํฐ ํน์ง ์ค ํ๋์ด๋ค. ๊ตฌ์ฑ ์์ ๊ฐ๋ฐ์๋ ๊ตฌ์ฑ ์์์ ์ฌ์ฉ ๋ฐฉ๋ฒ์ ์ ๊ฒฝ ์ฐ์ง ์๊ณ ๋ฐ์ธ๋ฉ ํ ์์ฑ์ ์ ์ํ๋ค. (์ผ๋ถ AngularJS 1.x๋ฅผ ์์ฑํ ๊ฒฝ์ฐ ์ฝ๊ฐ ์ฉ '@'๋ฐ '='๊ตฌ๋ฌธ์ด ๋ค๋ฅผ ์ ์๋ค.)
๋ฐ์ธ๋ฉ์ ํ์ดํ๋ฅผ ์ฌ์ฉํ ์ ๋ ์๋ค.
<div loggable [logText]="expression | uppercase">Hello</div>
// our directive will log the value of 'expression' in the component in uppercase
DOM ์์ฑ์ ๋์ผํ ์ด๋ฆ์ ๊ฐ์ง ์ง์์ด์ ์์ฑ์ ๋ฐ์ธ๋ฉ ํ๋ ค๋ฉด property : binding ๋์ ์์ฑ์ ์ธ ์ ์๋ค.
@Directive({
selector: '[loggable]',
inputs: ['logText']
})
export class SameNameInputDirective {
set logText(value) {
console.log(value);
}
}
<div loggable logText="Hello">Hello</div>
// our directive will log "Hello"
์ง์์ด์ @Input ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋ ฅ์ ์ ์ธํ๋ ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ด ์๋ค. ๋๋ ์ด๊ฒ์ ๊ฐ์ฅ ์ ํธ ํ๋ค. ๊ทธ๋์ ๋ง์ ์์ ๋ค์ด ์ง๊ธ๋ถํฐ ๊ทธ๊ฒ์ ์ฌ์ฉํ ๊ฒ์ด์ง๋ง, ๋น์ ์ด ์ ํธํ๋ ๊ฒ์ ์์ ๋กญ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
@Directive({
selector: '[loggable]'
})
export class InputDecoratorDirective {
@Input('logText') text: string;
}
๋๋ ๊ฐ์ ์๋ฏธ์ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ด ์๋ค.
@Directive({
selector: '[loggable]'
})
export class SameNameInputDecoratorDirective {
@Input() logText: string;
}
์ด ์์ ์ ๊ฐ๋ฅํ์ง๋ง ๊ฐ์ ์ด๋ฆ์ ํ๋์ ์ค์ ์๋ฅผ ์ฌ์ฉํ๋ฉด TypeScript ์ปดํ์ผ๋ฌ๊ฐ ์ค๋ฅ๋ก ํ๋จ ํ ์ ์๋ค. setter๊ฐ ํ์ํ ๊ฒฝ์ฐ ์์ ํ๋ ํ ๊ฐ์ง ๋ฐฉ๋ฒ์ ํญ์ ํ์ํ์ง๋ ์์ง๋ง @Input ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ setter์ ์ง์ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.
@Directive({
selector: '[loggable]'
})
export class InputDecoratorOnSetterDirective {
@Input('logText')
set text(value) {
console.log(value);
}
}
๋๋ setter ๊ทธ๋ฆฌ๊ณ ๋ฐ์ธ๋ฉ ์ด๋ฆ์ ๊ฐ์ด ํ๋ ๋ฐฉ๋ฒ๋ ์๋ค.
@Directive({
selector: '[loggable]'
})
export class SameNameInputDecoratorOnSetterDirective {
@Input()
set logText(value) {
console.log(value);
}
}
์ ๋ ฅ์ ์๋จ ์์์์ ํ๋จ ์์๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๋ฐ ํจ๊ณผ์ ์ด๋ค. ์๋ฅผ ๋ค์ด, ์กฐ๋๋ง ๋ชฉ๋ก์ ํ์ํ๋ ๊ตฌ์ฑ ์์๋ฅผ ์ํ ๊ฒฝ์ฐ ๋ชฉ๋ก์ด ํฌํจ ๋ ์์ ๊ตฌ์ฑ ์์์ ์กฐ๋๋ง์ ํ์ํ๋ ๋ค๋ฅธ ๊ตฌ์ฑ ์์๊ฐ ์์ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค.
@Component({
selector: 'ns-pony',
template: `<div>{{pony.name}}</div>`
})
export class PonyComponent {
@Input() pony: Pony;
}
@Component({
selector: 'ns-ponies',
template: `<div>
<h2>Ponies</h2>
// the pony is handed to PonyComponent via [pony]="currentPony"
<ns-pony *ngFor="let currentPony of ponies" [pony]="currentPony"></ns-pony>
</div>`
})
export class PoniesComponent {
ponies: Array<Pony> = [
{ id: 1, name: 'Rainbow Dash' },
{ id: 2, name: 'Pinkie Pie' }
];
}
Outputs
์ต๊ทผ ์์ ๋ก ๋์๊ฐ์ ์กฐ๋๋ง์ ํด๋ฆญํ์ฌ ์ ํํ๊ณ ์์ ๊ตฌ์ฑ ์์์ ์๋ฆฌ๊ณ ์ถ๋ค๊ณ ํฉ์๋ค. ์ด๋ฅผ ์ํด ๋ง์ถค ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์ด๊ฒ์ ์ค์ํ๋ค. Angular 2์์๋ ๋ฐ์ดํฐ๊ฐ ์์ฑ์ ํตํด ๊ตฌ์ฑ ์์๋ก ์ ์ ๋๊ณ ์ด๋ฒคํธ๋ฅผ ํตํด ๊ตฌ์ฑ ์์ ๋ฐ์ผ๋ก ์ด๋ํ ์ ์๋ค.
์ปค์คํ ์ด๋ฒคํธ๋ EventEmitter๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐฉ์ถ ๋๋ฉฐ, outputs ์์ฑ์ ์ฌ์ฉํ์ฌ ๋ฐ์ฝ๋ ์ดํฐ์์ ์ ์ธ ๋์ด์ผ ํ๋ค. inputs ์์ฑ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ง์์ด / ๊ตฌ์ฑ ์์์์ ๋ด ๋ณด๋ด๋ ค๋ ์ด๋ฒคํธ ๋ชฉ๋ก์ด ์๋ ๋ฐฐ์ด์ ํ์ฉํ๋ค.
ponySelected๋ผ๋ ์ด๋ฒคํธ๋ฅผ ๋ด๋ณด๋ด๋ ค๊ณ ํ๋ค๊ณ ๊ฐ์ ํด ๋ด ์๋ค. ์ฐ๋ฆฌ๋ ํ ์ผ์ด ์ธ ๊ฐ์ง ์๋ค. โข ๋ฐ์ฝ๋ ์ดํฐ์์ ์ถ๋ ฅ์ ์ ์ธํ๋ค. โข EventEmitter ๋ง๋ค๊ธฐ โข ์กฐ๋๋ง์ ์ ํํ๋ฉด ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๋ค.
@Component({
selector: 'ns-pony',
inputs: ['pony'],
// we declare the custom event as an output
outputs: ['ponySelected'],
// the method `selectPony()` will be called on click
template: `<div (click)="selectPony()">{{pony.name}}</div>`
})
export class SelectablePonyComponent {
pony: Pony;
// the EventEmitter is used to emit the event
ponySelected = new EventEmitter<Pony>();
/**
* Selects a pony when the component is clicked.
* Emits a custom event.
*/
selectPony() {
this.ponySelected.emit(this.pony);
}
}
ํ ํ๋ฆฟ์์ ์ฌ์ฉํ๋ ค๋ฉด :
<ns-pony [pony]="pony" (ponySelected)="betOnPony($event)"></ns-pony>
์์ ์์์๋ ์ฌ์ฉ์๊ฐ ์กฐ๋๋ง ์ด๋ฆ์ ํด๋ฆญ ํ ๋๋ง๋ค ํฌ๋ ๊ฐ (emit () ๋ฉ์๋์ ๋งค๊ฐ ๋ณ์)๋ก ์ด๋ฒคํธ ponySelected๋ฅผ ํธ์ถ ํ ๊ฒ์ด๋ค. ๋ถ๋ชจ ์์๋ ํ ํ๋ฆฟ์์ ํ์ธํ ์ ์๋๋ก ์ด ์ด๋ฒคํธ๋ฅผ ์์ ํ๊ณ ์๋ค. ์ด๋ฒคํธ $ event ๊ฐ์ ์ฌ์ฉํ์ฌ betOnPony ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. $ event๋ ๋น์ ์ด ๋ฐฉ์ถ ํ ์ด๋ฒคํธ์ ์ก์ธ์คํ๊ธฐ ์ํด ์ฌ์ฉํ๋ค : ์ฌ๊ธฐ์์ ๋ฐฉ์ถ๋๋ ๊ฐ์ ์กฐ๋๋ง์ด๋ค.
๊ทธ๋ฐ ๋ค์ ๋ถ๋ชจ ๊ตฌ์ฑ ์์์๋ betOnPony () ๋ฉ์๋๊ฐ ์์ด์ผ ํ๋ฉฐ, ์ ํํ ์กฐ๋๋ง๊ณผ ํจ๊ป ํธ์ถ ๋๋ค.
betOnPony(pony) {
// do something with the pony
}
์ํ๋ ๊ฒฝ์ฐ์๋ emitter: event ์ ๊ฐ์ด emiiter์ ๋ค๋ฅธ ์ด๋ฒคํธ ์ด๋ฆ์ ์ง์ ํ ์ ์๋ค.
@Component({
selector: 'ns-pony',
inputs: ['pony'],
// the emitter is called `emitter`
// and the event `ponySelected`
outputs: ['emitter: ponySelected'],
template: `<div (click)="selectPony()">{{pony.name}}</div>`
})
export class OtherSelectablePonyComponent {
pony: Pony;
emitter = new EventEmitter<Pony>();
selectPony() {
this.emitter.emit(this.pony);
}
}
์ ๋ ฅ์ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฒคํธ๋ฅผ ์ ์ธํ๋ ๊ฒ์ด ๊ฐ๋ฅํ์ง๋ง ๋น์ ์ด ์ํ๋ ๊ฒ์ ์ ํํด์ ์ ์ธํ๋ฉด ๋๋ค.
@Component({
selector: 'ns-pony',
template: `<div (click)="selectPony()">{{pony.name}}</div>`
})
export class SelectablePonyWithDecoratorComponent {
@Input() pony: Pony;
@Output() ponySelected = new EventEmitter<Pony>();
selectPony() {
this.ponySelected.emit(this.pony);
}
}
Lifecycle
๋น์ ์ ์ง์์๊ฐ ๊ทธ ์ถ์ ํน์ ์๊ฐ์ ๋ฐ์ํ๋ ๊ฒ์ ๋ฐ๋์ง๋ ๋ชจ๋ฅธ๋ค. ๊ณ ๊ธ ์ต์ ์ผ๋ก ๋งค๋ฒ ์ฌ์ฉํ์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ ๋นจ๋ฆฌ ๋๊ธธ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ ๊ตฌ์ฑ ์์์ ์ ๋ ฅ์ด ์์ฑ์์์ ์์ง ํ๊ฐํ์ง ์์ ๊ฒฝ์ฐ ์๋นํ ์๊ฐ์ ์ ์ฝ ํ ์ ์๋ค.
์ฆ, ๋ค์ ๊ตฌ์ฑ ์์๊ฐ ์๋ํ์ง ์๋๋ค.
@Directive({
selector: '[undefinedInputs]'
})
export class UndefinedInputsDirective {
@Input() pony: string;
constructor() {
console.log(`inputs are ${this.pony}`);
// will log "inputs are undefined", always
}
}
์๋ฅผ ๋ค์ด ์ ๋ ฅ ๊ฐ์ ์ก์ธ์คํ์ฌ ์๋ฒ์์ ์ถ๊ฐ์ ์ธ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๋ ค๋ฉด ๋ผ์ดํ ์ฌ์ดํด ๋จ๊ณ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. ์ฌ๋ฌ ๋จ๊ณ๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ ์์ฒด์ ์ธ ํน์์ฑ์ด ์๋ค.
โข ๋ฐ์ธ๋ฉ ๋ ์์ฑ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ngOnChanges๊ฐ ๋จผ์ ํธ์ถ๋ ๊ฒ์ด๋ค. SimpleChange์์ ๋ฐ์ธ๋ฉ๋ ํ์ฌ ๊ฐ๊ณผ ์ด์ ๊ฐ์ด ํฌํจ ๋ ์์ ๋งต์ ํ์ธํ๋ค. ํ์ธ ํ์ ๋ณ๊ฒฝ์ด ์์ผ๋ฉด ํธ์ถ๋์ง ์๋๋ค. โข ngOnInit ์ฒซ ๋ฒ์งธ ๋ณ๊ฒฝ ํ ํ ๋ฒ๋ง ํธ์ถ ๋๋ค. (ngOnChanges๋ ๋ชจ๋ ๋ณ๊ฒฝ์ ํธ์ถํ๋ค). ์ด ๋จ๊ณ๋ ์ด๋ฆ์์ ์ ์ ์๋ฏ์ด ์ด๊ธฐํ ์์ ์ ์ต์ ์ด๋ค. โข ngOnDestroy ๊ตฌ์ฑ ์์๊ฐ ์ ๊ฑฐ ๋ ๋ ํธ์ถ ๋๋ค. ์ ๋ฆฌํ๋ ์์ ์ ๋งค์ฐ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋๋ค.
๋ค๋ฅธ ๋จ๊ณ๋ ์ฌ์ฉํ ์ ์์ง๋ง ๊ณ ๊ธ ์ต์ ์ด๋ค.
โข ngDoCheck๋ ์ฝ๊ฐ ๋ค๋ฅด๋ค. ์กด์ฌํ๋ ๊ฒฝ์ฐ, ๊ฐ ๋ณ๊ฒฝ ๊ฒ์ ์ฃผ๊ธฐ์ ๋ถ๋ ค ๊ธฐ๋ณธ ๋ณ๊ฒฝ ๊ฐ์ง ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ ์ํ๋ค. ์ฆ, ๋ฐ์ธ๋ฉ ๋ ์์ฑ ๊ฐ์ ์ฐจ์ด๋ฅผ ์ฐพ๋๋ค. ๊ฐ. ์ฆ, ์ ์ด๋ ํ๋์ ์ ๋ ฅ์ด ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋ ์ ์ํฌ ๊ตฌ์ฑ ์์๊ฐ ๋ณ๊ฒฝ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋๊ณ , ๊ทธ ์์ด๋ ํ์ธ ๋ ๋ ๋๋ง์ด ๋๋ค. ํ์ง๋ง ์ ๋ ฅ์ด ๋ณ๊ฒฝ ๋์ด๋ ํจ๊ณผ๊ฐ ์๋ค๋ ๊ฒ์ ์๊ณ ์๋ ๊ฒฝ์ฐ๋ ๊ทธ๊ฒ์ ํด์ ํ ์ ์๋ค. ์ด๊ฒ์ ๊ธฐ๋ณธ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ์ง ์๊ณ ์ต์ ๊ฐ์ ํ์ธํ๋ ๊ฒ๋ง์ผ๋ก ๋ณ๊ฒฝ ๊ฐ์ง ์๋ํ๋ ค๋ ๊ฒฝ์ฐ ์ ์ฉํ์ง๋ง, ์ผ๋ฐ์ ์ผ๋ก ์ด๊ฒ์ ์ฌ์ฉํ์ง ์๋๋ค. โข ngAfterContentInit ๊ตฌ์ฑ ์์์ ๋ชจ๋ ๋ฐ์ธ๋ฉ์ด ์ฒ์ ํ์ธ ๋ ๊ฒฝ์ฐ์ ํธ์ถ๋๋ค. โข ngAfterContentChecked ๊ตฌ์ฑ ์์์ ๋ชจ๋ ๋ฐ์ธ๋ฉ์ด ํ์ธ ๋ ๋ ํธ์ถ๋๋ค. ๊ทธ๋ค์ ๋ณํ์ง ์๋๋ค. โข ngAfterViewInit ์์ ์ง์๋ฌธ์ ๋ฐ์ธ๋ฉ์ด ๋จผ์ ํ์ธ ๋ ๊ฒฝ์ฐ์ ํธ์ถ๋๋ค. โข ngAfterViewChecked ์์ ์ง์๋ฌธ์ ๋ฐ์ธ๋ฉ์ด ์ฒดํฌ ๋์ด์์ ๋ ๋ถ๋ ค ๋น๋ก ๋ณํํ์ง ์๊ณ . ๊ตฌ์ฑ ์์๊ฐ ํ์ ๊ตฌ์ฑ ์์์์ ๋ฌด์ธ๊ฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒฝ์ฐ์ ์ ์ฉํ๋ค. ngAfterViewInit ์ ๋ง์ฐฌ๊ฐ์ง๋ก ๊ตฌ์ฑ ์์์ ํฌํจ๋์ด ์์ผ๋ฉด ์๋ฏธ๊ฐ ์๋ค.
์ด์ ์ํ์ ngOnInit์ ์ฌ์ฉํ์ฌ ๋ ์ ๋์ํ๋ค. Angular 2๋ ngOnInit () ๋ฉ์๋๊ฐ ์์ผ๋ฉด ํด๋น ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฏ๋ก ์ง์๋ฌธ์ ์ด ๋ฉ์๋๋ฅผ ๊ตฌํํ๋ฉด ๋๋ค. ๋ชจ๋ฐ์ผ์ฉ TypeScript๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ฌ์ฉ ๊ฐ๋ฅํ ์ธํฐํ์ด์ค OnInit์ ํ์ฉํ์ฌ ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
@Directive({
selector: '[initDirective]'
})
export class OnInitDirective implements OnInit {
@Input() pony: string;
ngOnInit() {
console.log(`inputs are ${this.pony}`);
// inputs are not undefined \o/
}
}
์ด์ ์ฐ๋ฆฌ์ input ํ๊ทธ์ ์ ๊ทผํ ์ ์๋ค. ์์ฑ์ด ๋ณ๊ฒฝ ๋ ๋๋ง๋ค ๋ฌด์ธ๊ฐ๋ฅผ ํ๊ณ ์ถ๋ค๋ฉด ngOnChanges๋ฅผ ์ฌ์ฉํ์ฌ๋ผ.
@Directive({
selector: '[changeDirective]'
})
export class OnChangesDirective implements OnChanges {
@Input() pony: string;
ngOnChanges(changes: SimpleChanges) {
const ponyValue = changes['pony'];
console.log(`changed from ${ponyValue.previousValue} to ${ponyValue.currentValue}`);
console.log(`is it the first change? ${ponyValue.isFirstChange()}`);
}
}
๋ณ๊ฒฝ ๋งค๊ฐ ๋ณ์๋ ๋ฐ์ธ๋ฉ ์ด๋ฆ์ ํค๋ก ์ฌ์ฉํ๋ ๋งต์ด๋ฉฐ ๋ ์์ฑ (์ด์ ๊ฐ๊ณผ ํ์ฌ ๊ฐ)์ ๊ฐ์ผ๋ก ๊ฐ๋ SimpleChange ๊ฐ์ฒด๋ ๋ฌผ๋ก isFirstChange () ๋ฉ์๋๊ฐ ์๋์ง ์ฌ๋ถ๋ฅผ ์๊ธฐ์ํ ์ฒซ ๋ฒ์งธ ๋ณ๊ฒฝ ์ฌํญ์ด๋ค. ๋ฐ์ธ๋ฉ ์ค ํ๋์ ๋ณ๊ฒฝ์๋ง ๋ฐ์ํ๋ ค๋ฉด setter๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค. ๋ค์ ์์ ๋ ์ด์ ์ถ๋ ฅ๊ณผ ๋์ผํ ์ถ๋ ฅ์ ์์ฑํ๋ค.
@Directive({
selector: '[setterDirective]',
inputs: ['pony']
})
export class SetterDirective {
private ponyModel: string;
set pony(newPony) {
console.log(`changed from ${this.ponyModel} to ${newPony}`);
this.ponyModel = newPony;
}
}
ngOnChanges๋ ๋์์ ์ฌ๋ฌ ๋ฐ์ธ๋ฉ์ ๋ณผ ๋ ์ ์ฉํ๋ค. ํ๋ ์ด์์ ๋ฐ์ธ๋ฉ์ด ๋ณ๊ฒฝ๋๊ณ ๋ณ๊ฒฝ๋ ์์ฑ๋ง ํฌํจํ๋ ๊ฒฝ์ฐ์ ํธ์ถ๋๋ค.
ngOnDestroy ๋จ๊ณ๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ ์ ์ทจ์ํ๋ ๊ฒ๊ณผ ๊ฐ์ด ๊ตฌ์ฑ ์์๋ฅผ ์ ๋ฆฌํ๋ ๋ฐ ์ ํฉํ๋ค. ์ฌ๊ธฐ์์ OnDestroyDirective๋ ์์ฑ ๋ ๋๋ง๋ค "hello"๋ฅผ ๊ธฐ๋กํ๋ค. ๊ตฌ์ฑ ์์๊ฐ ํ์ด์ง์์ ์ ๊ฑฐ๋๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์ถ์ ํผํ๊ธฐ ์ํด setInterval์ ์ค์งํ๋ ค๊ณ ํ๋ค.
@Directive({
selector: '[destroyDirective]'
})
export class OnDestroyDirective implements OnDestroy {
sayHello: number;
constructor() {
this.sayHello = window.setInterval(() => console.log('hello'), 1000);
}
ngOnDestroy() {
window.clearInterval(this.sayHello);
}
}
Providers
Dependency Injection ์ฅ์ ๊ณต๊ธ์์ ๊ดํด ์ด๋ฏธ ์ด์ผ๊ธฐ ํ์ ์ด ์๋ค. ์ด ์์ฑ์ ํ์ฌ ์ง์์ด์ ๊ทธ ์์์ ์ฃผ์ฌ ํ ์์๋ ์๋น์ค๋ฅผ ์ ์ธ ํ ์์๊ฒ ํ๋ค.
@Directive({
selector: '[providersDirective]',
providers: [PoniesService]
})
export class ProvidersDirective {
constructor(poniesService: PoniesService) {
const ponies = poniesService.list();
console.log(`ponies are: ${ponies}`);
}
}
Components
Component๋ ์ง์๋ฌธ๊ณผ ์ค์ ๋ก ๋ค๋ฅด์ง ์๋ค. ๋จ์ง ๋ ๊ฐ์ ์ ํ์ ์์ฑ์ด ์์ผ๋ฉฐ ๊ด๋ จ ๋ทฐ๊ฐ ์์ด์ผ ํ๋ค. ์ง์์ด์ ๋น๊ตํ์ฌ ๋ง์ ์๋ก์ด ์์ฑ์ ๊ฐ์ ธ ์ค์ง๋ ์๋๋ค.
View providers
์ฐ๋ฆฌ๋ ๊ณต๊ธ์๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ํ ์ฃผ์ ์ ํ ์ ์๋ ๊ฒ์ ํ์ธํ์๋ค. viewProviders๋ ๋งค์ฐ ์ ์ฌํ๋ provider๋ค์ ํ์ฌ ์ปดํฌ๋ํธ์ ํ์ฉ๋์ง ์์ ์ปดํฌ๋ํธ์๋ ์ ์ฉํ์ง ์๋๋ค.
Template / Template URL
์ฐ๋ฆฌ๋ ๊ณต๊ธ์๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ํ ์ฃผ์ ์ ํ ์ ์๋ ๊ฒ์ ํ์ธํ์๋ค. viewProviders๋ ๋งค์ฐ ์ ์ฌํ๋ provider๋ค์ ํ์ฌ ์ปดํฌ๋ํธ์ ํ์ฉ๋์ง ์์ ์ปดํฌ๋ํธ์๋ ์ ์ฉํ์ง ์๋๋ค. @Component์ ์ฃผ์ ๊ธฐ๋ฅ์ ํ ํ๋ฆฟ์ ๊ฐ๋ ๋ฐ๋ฉด ์ง์๋ฌธ์๋ ํ ํ๋ฆฟ์ด ์๋ ๊ฒ์ด ์ฐจ์ด ์ด๋ค. ํ ํ๋ฆฟ์ ์ธ๋ผ์ธ์ผ๋ก ์ ์ธํ๊ฑฐ๋ templateURL์ ์ฌ์ฉํ์ฌ URL์ ์ฌ์ฉํ์ฌ ๋ณ๋์ ํ์ผ์ ๋ฃ์ ์ ์๋ค. (๋จ, ๋์์ ๋ ๋ค ํ ์๋ ์๋ค)
์ผ๋ฐ์ ์ผ๋ก ํ ํ๋ฆฟ์ด ์์ผ๋ฉด (1-2 ์ค) ์ธ๋ผ์ธ์ผ๋ก ์ ์งํ๋ ๊ฒ์ด ๋ ๋ซ๋ค. ์ฝ๋๊ฐ ๋์ด๋๊ธฐ ์์ํ๋ฉด ๊ตฌ์ฑ ์์๊ฐ ๋ณต์กํด์ง์ง ์๋๋ก ํ์ผ์ ์์ฒด ํ์ผ๋ก ์ด๋ํ์ฌ๋ผ. URL, ์๋ URL ๋๋ ์ ์ฒด HTTP URL์ ๋ํด ์ ๋ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ๊ตฌ์ฑ ์์๊ฐ ๋ก๋ ๋๋ฉด Angular 2๊ฐ URL์ ํ์ธํ๊ณ ํ ํ๋ฆฟ์ ๊ฐ์ ธ ์ค๋ ค๊ณ ์๋ํ๋ค. ์ฑ๊ณตํ ๊ฒฝ์ฐ ํ ํ๋ฆฟ์ ๊ตฌ์ฑ ์์์ ์๋์ฐ ๋ฃจํธ์ด๋ฉฐ ํด๋น ํํ์์ด ํ๊ฐ ๋๋ค. ํฐ ๊ตฌ์ฑ ์์๊ฐ ์๋ ๊ฒฝ์ฐ ์ผ๋ฐ์ ์ผ๋ก ํ ํ๋ฆฟ์ ๊ฐ์ ํด๋์ ๋ณ๋ ํ์ผ์ ์ ์ฅํ๊ณ ์๋ URL์ ์ฌ์ฉํ์ฌ ๋ก๋ํ๋ค.
@Component({
selector: 'ns-templated-pony',
templateUrl: 'components/pony/templated-pony.html'
})
export class TemplatedPonyComponent {
@Input() pony: any;
}
์๋ URL์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ URL์ ์ฑ์ ๊ธฐ๋ณธ URL์ ์ฌ์ฉํ์ฌ ํด๊ฒฐ๋๋ค. ๊ตฌ์ฑ ์์๊ฐ ๋๋ ํ ๋ฆฌ ๊ตฌ์ฑ ์์ / ํฌ๋์ ์์ผ๋ฉด ํ ํ๋ฆฟ URL์ components / pony / pony.html์ด ๋๋ฏ๋ก URL์ด ๋ฒ๊ฑฐ๋ก์ธ ์ ์๋ค. ๊ทธ๋ฌ๋ moduleId ๋ฑ๋ก ์ ๋ณด๋ฅผ ์ฌ์ฉํ์ฌ CommonJS ๋ชจ๋์ ์ฌ์ฉํ์ฌ ์์ฉ ํ๋ก๊ทธ๋จ์ ํจํค์งํ๋ฉด ์ฝ๊ฐ ๋ ์ ์ํ ํ ์ ์๋ค. ์ด ๊ฐ์ CommonJS๊ฐ ๋ฐํ์์ ์ค์ ํ๋ ๊ฐ์ธ module.id ์ด๋ค. Angular 2๋์ด ๊ฐ์ ์ฌ์ฉํ์ฌ ์ ํํ ์๋ URL์ ๋ง๋ค ์ ์๋ค. ํ ํ๋ฆฟ URL์ ์ด์ ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ผ ๊ฒ ์ด๋ค.
@Component({
selector: 'ns-templated-pony',
templateUrl: 'templated-pony.html',
moduleId: module.id
})
export class ModuleIdPonyComponent {
@Input() pony: any;
}
๊ทธ๋ฆฌ๊ณ ๊ตฌ์ฑ ์์์ ๋์ผํ ๋๋ ํ ๋ฆฌ์์ ํ ํ๋ฆฟ์ ์ฐพ์ ์ ์์ต๋๋ค! ๋ ๋์ ์ ์ : Web-Pack์ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, ์ฝ๊ฐ์ ์ค์ (์ด๋ฏธ Angular-Cli๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด ๊ฐ๋ฅํฉ๋๋ค.)์ผ๋ก module.id๋ฅผ ์ ๊ฑฐํ๊ณ ์ง์ ์๋ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค. Webpack์ ์์ ํ URL์ ์์๋ผ ์ ์๋ค.
Styles / Styles URL
๊ตฌ์ฑ ์์์ ์คํ์ผ์ ์ง์ ํ ์๋ ์๋ค. ์ค์ ๋ก ๋ถ๋ฆฌ ๋ ๊ตฌ์ฑ ์์๋ฅผ ๊ณํํ๋ ๊ฒฝ์ฐ ํนํ ์ ์ฉํ๋ค. styles ๋๋ styleUrl์ ์ฌ์ฉํ์ฌ ์ด๋ฅผ ์ง์ ํ ์ ์๋ค.
์๋์์ ๋ณผ ์ ์๋ฏ์ด styles ์์ฑ์ CSS ๊ท์น์ ๋ฐฐ์ด์ ๋ฌธ์์ด๋ก ์ทจํ๋ค. ๊ฝค ๋นจ๋ฆฌ ์๋ ์ ์๋ค๊ณ ์์ํ ์ ์์ผ๋ฏ๋ก ๋ณ๋์ ํ์ผ๊ณผ styleUrl์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค. ํ์์ ์ด๋ฆ์์ ์ ์ ์๋ฏ์ด URL ๋ฐฐ์ด์ ์ง์ ํ ์ ์๋ค.
@Component({
selector: 'ns-styled-pony',
template: '<div class="pony">{{pony.name}}</div>',
styles: ['.pony{ color: red; }']
})
export class StyledPonyComponent {
@Input() pony: any;
}
Declarations
@NgModule์ ์ ์ธ์์ ์ฌ์ฉํ๊ณ ์๋ ๋ชจ๋ ์ง์๋ฌธ๊ณผ ๊ตฌ์ฑ ์์๋ฅผ ์ ์ธํด์ผ ํ๋ค๋ ๊ฒ์ ๊ธฐ์ตํด๋ผ. ๊ทธ๋ ๊ฒ ํ์ง ์์ผ๋ฉด ํ ํ๋ฆฟ์์ ๊ตฌ์ฑ ์์๊ฐ ์ ํ ๋์ง ์์ผ๋ฉฐ ์ด์ ๋ฅผ ํ์ ํ๋ ๋ฐ ๋ง์ ์๊ฐ์ ๋ญ๋น ํ๋ค.
๋ ๊ฐ์ง ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ์ค์๋ ์ง์๋ฌธ์ ์ ์ธํ๋ ๊ฒ์ ์์ด ๋ฒ๋ฆฌ๊ณ ์๋ชป๋ ์ ํ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค. ์ ์๋ฌด ์ผ๋ ์ผ์ด๋์ง ์๋๋ค๋ฉด, ์ด๊ฒ๋ค์ ์ฃผ์ํ๋๋ก ํด๋ผ!! ์ฐ๋ฆฌ๋ ์ฟผ๋ฆฌ, ๋ณ๊ฒฝ ๊ฐ์ง, ๋ด๋ณด๋ด๊ธฐ, ์บก์ํ ์ต์ ๋ฑ๊ณผ ๊ฐ์ ๋ช ๊ฐ์ง ์ฌํญ์ ๋จ๊ฒจ ๋์๋ค. ๊ณ ๊ธ ์ต์ ์ด๋ฏ๋ก ์ฆ์ ํ์ํ์ง ์์ง๋ง ์ฐ๋ฆฌ๋ ๊ณง ๊ณ ๊ธ ์ฅ์์ ๊ทธ๋ค์ ํ์ธ ํ ์ ์๋ค!
Reference URL
Last updated
Was this helpful?