Component Lifecycle
이번 포스트에서는 Angular의 Lifecycle
에 대해서 알아보겠습니다. 다른 Framework과 마찬가지로 Angular 역시 여러 단계의 lifecycle을 관리합니다. Component와 Directive가 이 lifecycle의 영향을 받게 되며 각 lifecycle마다 제공되는 hook method를 이용하여 특정 작업을 처리할 수 있습니다.
이런 hook method는 interface형태로 우리에게 제공됩니다. Component 혹은 Directive class가 이 interface를 구현하고 그 안의 특정 method를 overriding하는 식으로 hook method를 이용할 수 있습니다.
Component를 대상으로 객체가 생성되고 소멸되기까지 호출되는 hook method를 순서대로 나열하면 다음과 같습니다. Directive는 View를 가지고 있지 않기 때문에 ngAfter로 시작되는 hook method는 호출되지 않습니다.
- constructor
- ngOnChanges
- ngOnInit
- ngDoCheck
- ngAfterContentInit
- ngAfterContentChecked
- ngAfterViewInit
- ngAfterViewChecked
- ngOnDestroy
간단하게 Project를 하나 생성해서 각 lifecycle단계에서 해당 hook method가 호출되는지 확인하는 식으로 진행하시면 됩니다.
constructor
Component 혹은 Directive가 생성될 때 호출됩니다. 사실 constructor는 Angular의 lifecycle의 단계에 포함될 내용은 아닙니다.
TypeScript에서는 일반적으로 constructor에서 초기화를 진행합니다. 하지만 Angular에서 사용하는 속성의 초기화는 ngOnInit
에서 하는것이 좋습니다.
ngOnChanges
@Input
을 이용해 부모 Component가 자식 Component에게 데이터를 전달할 수 있습니다. ngOnChanges
는 부모 Component에서 자식 Component로 데이터가 binding 될 때 혹은 변경되었을 때 호출됩니다. 따라서 @Input
을 사용하지 않으면 호출되지 않습니다.
정확하게는 부모 Component로부터 자식 Component에게 전달하는 primitive 값이 변경되거나 혹은 참조하는 객체의 reference가 변경되어야 호출됩니다. 즉, 참조하는 객체의 property가 변경되는 경우에는 ngOnChanges가 호출되지 않는다는 것 기억하셔야 합니다.
@Input
을 이용한 값의 binding은 생성자가 호출된 후에 일어납니다. 즉, 생성자에서 @Input을 이용해 binding한 값을 출력하면 undefined
가 출력되게 됩니다. 간단한 이벤트 처리를 통해 @Input으로 전달되는 값을 변경해보면 값이 변경될 때마다 ngOnChanges hook method가 호출되는걸 확인할 수 있습니다.
ngOnChanges hook method의 인자로 SimpleChanges
객체를 하나 받을 수 있습니다. 해당 객체를 이용하면 변경되기 이전값과 이후값등을 알 수 있습니다.
import {Component, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit, OnChanges {
@Input() myInput: string;
constructor() {
console.log(`Constructor 호출!! => myInput : ${this.myInput}`);
}
ngOnChanges(simpleChanges: SimpleChanges) {
console.log(`ngOnChanges 호출!! => myInput : ${this.myInput}`);
console.log(simpleChanges.myInput.previousValue);
console.log(simpleChanges.myInput.currentValue);
}
ngOnInit() {
}
}
ngOnInit
ngOnInit
는 ngOnChanges가 호출된 이후에 모든 속성에 대한 초기화가 완료된 시점에 딱 한번만 호출됩니다. 즉, class가 가지고 있는 속성과 @Input을 통해 값을 내려받은 속성이 모두 초기화가 된 이후에 호출됩니다. 결국 Component의 속성 참조는 ngOnInit hook method이후에 참조하는 것이 좋습니다.
결국 생성자는 Service의 Injection같은 사항을 처리하고 속성에 대한 초기화는 ngOnInit에서 처리하시는게 좋다는 말입니다.
import {Component, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit, OnChanges {
@Input() myInput: string;
myString = 'Hello';
constructor() {
console.log(`Constructor 호출!! => myInput : ${this.myInput}`);
console.log(`Constructor 호출!! => myString : ${this.myString}`);
}
ngOnChanges(simpleChanges: SimpleChanges) {
console.log(`ngOnChanges 호출!! => myInput : ${this.myInput}`);
console.log(simpleChanges.myInput.previousValue);
console.log(simpleChanges.myInput.currentValue);
}
ngOnInit() {
console.log(`ngOnInit 호출!! => myInput : ${this.myInput}`);
console.log(`ngOnInit 호출!! => myString : ${this.myString}`);
}
}
ngDoCheck
ngOnInit hook method가 호출된 이후에 호출됩니다. Component에서 발생하는 모든 상태변화에 반응하여 호출되어지는 hook method로 Angular의 Changes Detection이 상태변화를 감지하면 자동으로 호출되게 됩니다. 한가지 주의하셔야 할 점은 ngOnChanges와는 다르게 primitive값의 변경, reference 객체의 변경, reference객체의 속성변경에 대한 모든 변경에 대해 해당 hook mehtod가 호출된다는 점입니다. 심지에 이전값과 같은 값이 assign되었음에도 호출됩니다. 따라서 ngDoCheck
을 많이 사용하게 되면 그만큼 성능이 저하될 수 있습니다.
import {Component, DoCheck, Input,
OnChanges, OnInit, SimpleChanges} from '@angular/core';
interface IBook {
btitle: string;
bauthor: string;
}
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit, OnChanges, DoCheck {
@Input() myInput: IBook;
myString = 'Hello';
constructor() {
console.log(`Constructor 호출!! => myInput : ${this.myInput}`);
console.log(`Constructor 호출!! => myString : ${this.myString}`);
}
ngOnChanges(simpleChanges: SimpleChanges) {
console.log(`ngOnChanges 호출!! => myInput : ${this.myInput}`);
console.log(simpleChanges.myInput.previousValue);
console.log(simpleChanges.myInput.currentValue);
}
ngOnInit() {
console.log(`ngOnInit 호출!! => myInput : ${this.myInput}`);
console.log(`ngOnInit 호출!! => myString : ${this.myString}`);
}
ngDoCheck() {
console.log(`ngDoCheck 호출!! => myInput : ${this.myInput}`);
console.log(`ngDoCheck 호출!! => myString : ${this.myString}`);
}
}
ngAfterContentInit, ngAfterContentChecked
최초의 ngDoCheck hook method가 호출된 후에 한번만 호출되며 앞서 배운 ngContent directive를 이용해 부모 Component의 template 일부를 자식 Component에서 projection한 후 호출됩니다. 여기서 Content의 의미는 ngContent directive처럼 외부에서 Component View안으로 내용을 가져온 것을 지칭합니다. 이 hook method 이후에 Change Detection이 실행된 후 바로 따라서 ngAfterContentChecked hook method가 호출됩니다.
ngAfterViewInit, ngAfterViewChecked
Component에 속한 모든 View와 ViewChild가 시작되고 나서 호출됩니다. 쉽게 생각하면 HTML이 모두 화면에 출력된 후 호출된다고 생각 하시면 됩니다. ngAfterViewChecked는 Component의 View에 대한 Change Detection이 실행되고 난 후 호출됩니다.
ngOnDestroy
Component가 소멸하기 직전에 호출됩니다. 일반적으로 사용된 자원에 대한 해제 코드가 들어옵니다.
import {
AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit,
Component, DoCheck, Input, OnChanges, OnDestroy, OnInit,
SimpleChanges
} from '@angular/core';
interface IBook {
btitle: string;
bauthor: string;
}
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit, OnChanges, DoCheck, AfterContentInit,
AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy {
@Input() myInput: IBook;
myString = 'Hello';
constructor() {
console.log(`Constructor 호출!! => myInput : ${this.myInput}`);
console.log(`Constructor 호출!! => myString : ${this.myString}`);
}
ngOnChanges(simpleChanges: SimpleChanges) {
console.log(`ngOnChanges 호출!! => myInput : ${this.myInput}`);
console.log(simpleChanges.myInput.previousValue);
console.log(simpleChanges.myInput.currentValue);
}
ngOnInit() {
console.log(`ngOnInit 호출!! => myInput : ${this.myInput}`);
console.log(`ngOnInit 호출!! => myString : ${this.myString}`);
}
ngDoCheck() {
console.log(`ngDoCheck 호출!! => myInput : ${this.myInput}`);
console.log(`ngDoCheck 호출!! => myString : ${this.myString}`);
}
ngAfterContentInit() {
console.log(`ngAfterContentInit 호출!!`);
}
ngAfterContentChecked() {
console.log(`ngAfterContentChecked 호출!!`);
}
ngAfterViewInit() {
console.log(`ngAfterViewInit 호출!!`);
}
ngAfterViewChecked() {
console.log(`ngAfterViewChecked 호출!!`);
}
ngOnDestroy() {
console.log(`ngOnDestroy 호출!!`);
}
}
이번 포스트는 Angular가 제어하는 Component와 Directive의 lifecycle에 대해서 살펴봤습니다. 어떤 시점에 어떤 hook method가 호출되는지 이해하고 Change Detection이 어느 시점에 호출되는지를 이해하시면 조금 더 Angular를 이해하는데 도움이 될 듯 합니다.
'Web Programming > Angular - TypeScript' 카테고리의 다른 글
앵귤러JS (AngularJS) 란? (0) | 2018.08.29 |
---|---|
Angular 강좌 및 참고사이트 (0) | 2018.08.28 |
Angular Pipe (0) | 2018.08.28 |
Angular Directive (0) | 2018.08.28 |
Angular Material Table Event (0) | 2018.08.28 |