일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- angular
- php
- Visual Studio Code
- NVM
- Ionic
- 변화감지
- getElementsByClassName
- VSCode
- IONIC3
- sealize
- JavaScript
- rxjs
- 이미지바꾸기
- Sentry
- angular5
- zsh
- ChangeDetectorRef
- border-width
- aab 배포
- oh-my-zsh
- Git
- 테두리굵기
- fromEvent
- 자바스크립개념
- error
- hashchange
- typescript
- change detection
- code .
- ion-range
- Today
- Total
hsunny study blog
[Angular] 컴포넌트가 누적되어 메모리에서 제거되지 않는 경우들 (즉, 메모리 누수) 본문
규모가 큰 서비스에서는 메모리 해제가 중요하다. 메모리가 쌓이다보면 앱이 느려지는 문제가 발생한다.
Angular로 개발을 하면서, 메모리의 누수를 확인하기 위해 Google Development Tool 안에 있는 Memory 탭과 Performace monitor를 사용할 수 있다.
Angular의 경우 이 누수를 피하기 위해 어떤 것들을 봐야하는지 살펴보자.
1. Component 데코레이터의 provider에 직접 추가한 service가 destory 되지 않은 경우
일반적인 서비스라면 Component 단에서 서비스를 주입했다면, Component의 생명주기를 따라간다. 하지만 예외가 있다.
서비스 파일의 상단에 아래 코드를 적었다면..
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // ****
})
export class MyService {
constructor() { }
sayHello() {
console.log('Hello, Angular!');
}
}
MyService 클래스는 root 단에 주입한 서비스다.
이 점을 간과하고, Component에서 주입을 추가하면..
import { Component } from '@angular/core';
import { MyService } from './my.service';
@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
providers: [MyService] // ****
})
export class MyComponent {
constructor(private myService: MyService) {
myService.sayHello();
}
}
Component 생명주기를 따라가지 않는다.
이로 인해 이 서비스를 주입한 Component가 계속해서 살아있고, 심지어 컴포넌트에 들어오는(Init) 수만큼 컴포넌트가 쌓이는 문제가 발생한다. 컴포넌트만 쌓이냐? 아니다. 컴포넌트에서 주입한 서비스들도 덩달아 쌓인다.
만약 인터넷이 끊겼다가 다시 연결되는 로직이 서비스에 있다면, 컴포넌트에 들어온 횟수만큼 API 통신을 하게 된다.
조심하자.
이 문제를 해결하려면,
1. root 단에서 주입하지 않거나
2. root 단에서 주입할거면, component 단에서 주입하지 않거나
둘 중에 상황에 적합한 방식을 선택해야 한다.
2. Component의 nativeElement에 접근하여, DOM을 직접 수정한 경우
Angular에서는 Renderer2 를 사용하여 DOM을 수정해주어야 한다. 그러지 않으면 Angular의 생명주기대로 관리가 되지 않는다.
만약 코드 중에 아래와 같이 class를 지우고, 혹은 추가한게 있는가?
import { ElementRef } from '@angular/core';
//...
this.elementRef.nativeElement.classList.remove('is-selected');
this.elementRef.nativeElement.classList.add('is-selected');
그렇다면 아래와 같이 변경해보자.
import { ElementRef, Renderer2 } from '@angular/core';
//...
this.renderer.removeClass(this.elementRef.nativeElement, 'is-selected');
this.renderer.addClass(this.elementRef.nativeElement, 'is-selected');
이외에도 여러 케이스가 있지만, 최근에 경험했던 케이스들을 공유해본다.
누군가에게는 도움이 되길 바라며!