Angular Datagrid 응용 프로그램을 최적화하고 만드는 방법 > 시티즌 인사이트

본문 바로가기

시티즌 커뮤니티

시티즌 인사이트

IT&개발 정보 Angular Datagrid 응용 프로그램을 최적화하고 만드는 방법

페이지 정보

작성자 GrapeCity 작성일 22-05-30 10:50 조회 133회 댓글 0건

본문

최적화는 인터넷 응용 프로그램을 구축할 때 디자인 프로세스의 중요한 부분입니다.


연구 결과에 따르면, 웹 사이트에서 페이지를 로드하는 데 이상적인 시간은 2~3초입니다. 그보다 길어지면 로드가 끝나기도 전에 사용자가 페이지에서 나갈 가능성이 현저히 높아지며, 페이지가 더 빠르게 로드될수록 사용자 기반의 광고 수익을 늘리는 것으로 나타납니다.


모바일 장치에서 페이지를 방문하는 경우에는 최적화가 훨씬 중요한 문제가 됩니다. 로드 시간이 길어지면 데스크톱에서 로드하는 페이지의 경우보다 이탈률이 훨씬 높아집니다. 모바일 장치에서 사이트에 방문하는 사용자 수가 늘어남에 따라 Google은 모바일 우선 인덱싱 선호로 방향을 전환했습니다.


즉, Google이 Googlebot의 스마트폰 에이전트 기반의 웹사이트 인덱싱을 더 선호하게 됨에 따라 최적화가 훨씬 더 중요해졌다는 의미입니다.


오늘날의 대형 응용 프로그램은 대용량 데이터 집합과 대용량 번들을 취급하고 수많은 복잡한 계산을 수행합니다. 다행히 Angular와 Wijmo의 FlexGrid에서 Angular DataGrid 응용 프로그램을 최적화할 수 있는 옵션을 제공합니다. 이 블로그에서는 다음과 같은 주제를 다루겠습니다.

  • Observable에서 구독 해제

  • 확인자를 사용하여 라우팅 흐름 관리

  • ChangeDetectionStrategy.onPush로 변경 감지 최적화

  • FlexGrid 가상화로 DOM 요소 감소

  • Angular 모듈 지연 로드


Angular에서 FlexGrid를 쉽게 사용할 수 있으며 재미있기도 합니다!

믿을 수 없으면 Wijmo 무료 평가판을 다운로드하여 따라 해 보세요.



Observable에서 구독 해제


Observable은 말하는 그대로 합니다.

즉, 사용자가 관찰하고 해당 조치를 취하려는 것입니다.

개발자는 Observable을 사용하여 데이터를 응용 프로그램과 응용 프로그램 주위로 쉽게 전달할 수 있습니다.


Angular에서는 이벤트 처리, 비동기 프로그래밍 그리고 다양한 데이터 집합 관리에 사용됩니다. 그러나 Observable을 잘못 사용하면 성능이 저하되고 심각한 메모리 관리 문제가 발생할 수 있습니다.


Observable을 사용할 때는 데이터를 얻고 데이터에 변동이 있는지 확인하기 위해 구독 메서드를 사용하는데, 이 모두가 비동기로 이루어집니다.


일반적으로 사용자는 데이터를 얻기 위해 HTTP 호출을 수행하는 서비스의 메서드를 구독하며, 대부분의 경우 이 작업은 컴포넌트의 ngOnInit() 메서드 내에서 수행됩니다.


data.service.ts:

export class DataService {
  apiURL = 'https://mocki.io/v1/6f3bb49a-353a-4318-ba77-0474f6fafeed';
​
  constructor(private http: HttpClient) {}
​
  getAPIData() {
      return this.http.get(this.apiURL);
  }
}


mock.component.ts:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { DataService } from './data.service';
​
@Component({...})
export class MockComponent implements OnInit, OnDestroy {
  mockData: any;
  mockSubscription: any;
  constructor(private dataService: DataService){}
​
  ngOnInit() {
      this.mockSubscription = this.dataService.getAPIData().subscribe((data) => {
          this.mockData = data;
      });
  }
}


이 단계가 완료되면 이제 API에서 검색하는 데이터에 비동기적으로 액세스하고 데이터의 변경 사항을 확인할 수 있습니다.


그러나 우리가 놓치고 있는 중요한 점이 하나 있습니다. 바로 Observable에 대해 unsubscribe() 메서드를 호출하는 것입니다.


이 컴포넌트에서 벗어나는 경우에도 해당 구독 연결은 계속 열린 상태로 유지되어 메모리가 누수되고 심각한 성능 및 보안 문제로 이어지게 됩니다.


이 컴포넌트가 로드된 상태에서는 이 연결이 계속 유지되기를 원하므로 우리는 컴포넌트가 제거되어 구독이 해제될 때까지 계속 기다릴 것입니다. 위의 코드 샘플을 보고 눈치를 채셨겠지만, 컴포넌트가 제거될 때마다 호출되는 라이프사이클 후크인 OnDestroy도 가져옵니다.


우리가 할 일은 ngOnDestroy() 메서드를 구현하고 Observable에 대해 unsubscribe()를 호출하는 것입니다.


mock.component.ts:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { DataService } from './data.service';
​
@Component({...})
export class MockComponent implements OnInit, OnDestroy {
  mockData: any;
  mockSubscription: any;
  constructor(private dataService: DataService){}
​
  ngOnInit() {
      this.mockSubscription = this.dataService.getAPIData().subscribe((data) => {
          this.mockData = data;
      });
  }
​
  ngOnDestroy() {
      this.mockSubscription.unsubscribe();
  }
}


사용자가 이 컴포넌트에서 나가면 응용 프로그램은 메모리 누수를 방지하기 위해 Observable에서 구독을 해제하게 됩니다.


Observable에 대해 자세히 알아보려면 추가 정보를 제공하는 Angular 문서를 참조하세요.



확인자를 사용하여 라우팅 흐름 관리


Observable을 활용하여 HTTP 호출을 통해 데이터를 검색하는 컴포넌트를 로드할 때 HTTP 요청에서 오류가 수신되는 시점이 있을 것입니다. 이런 경우에는 성능에 영향을 미치는 다음 몇 가지 작업이 발생하게 됩니다.

  • 현재 로드된 컴포넌트가 제거됩니다.

  • 새 컴포넌트가 보기에 로드됩니다.

  • 새 컴포넌트가 HTTP 요청을 전송하여 오류가 발생합니다.

  • 새 컴포넌트가 제거됩니다.

  • 이전 컴포넌트가 다시 로드되고 오류 메시지를 표시합니다.


DOM이 여러 요소를 렌더링 및 제거하는 이러한 작업이 이루어집니다. 이것은 비싼 대가를 치르는 연산 집합입니다. 다행스러운 점은 Angular가 이러한 DOM 작업이 처리되지 않도록 하는 방법인 확인자를 제공한다는 것입니다.


확인자는 링크를 클릭하는 사용자와 컴포넌트를 로드하는 응용 프로그램 간에 실행되는 중간 코드 역할을 합니다. 그 의미를 이해하려면 두 종류의 라우팅 흐름을 확인해 보시기 바랍니다. 하나는 확인자를 사용하지 않는 경우이고, 다른 하나는 확인자가 구현된 경우입니다.


표준 라우팅 흐름:

  1. 사용자는 링크를 클릭하여 새 페이지로 이동합니다.

  2. 응용 프로그램은 해당 컴포넌트를 렌더링합니다.


확인자 라우팅 흐름:

  1. 사용자는 링크를 클릭하여 새 페이지로 이동합니다.

  2. 응용 프로그램은 특정 코드를 실행하고 Observable을 반환합니다.

  3. 반환된 Observable은 새 컴포넌트의 생성자 또는 ngOnInit()에 저장됩니다.

  4. 컴포넌트가 로드됩니다.


확인자의 멋진 점은 Observable에서 HTTP 오류가 수신되면 확인자는 컴포넌트를 로드하지 않는다는 것입니다. 이런 방법으로 DOM이 다양한 요소를 모두 제거하고 렌더링하는 프로세스를 진행하지 못하도록 방지합니다. 확인자를 구현하려면 아래 코드를 확인하세요.


app-routing.module.ts

const routes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'mock', component: MockComponent, resolve: { mock: MockResolverService } }
];


mockresolver.service.ts

export class MockResolverService implements Resolve<any> {
  constructor(private dataService: DataService) {}
​
  resolve(route: ActivatedRouteSnapshot): Observable<any> {
      return this.dataService.getAPIData().pipe(
          catchError(error => { return of('No data returned...'); })
      );
  }
}


mock.component.ts

export class MockComponent implements OnInit, OnDestroy {
  mockData: any;
  mockSubscription: any;
  constructor(private activatedRoutes: ActivatedRoute) {}
​
  ngOnInit() {
      this.mockSubscription = this.activatedRoutes.data.subscribe((response: any) => {
          this.mockData = response;
      });
  }
​
  ngOnDestroy() {
      this.mockSubscription.unsubscribe();
  }
}


확인자 및 확인자의 모든 역할에 대해 자세히 알아보려면 Angular 문서를 확인하세요.



ChangeDetectionStrategy.OnPush로 변경 감지 최적화


CD(변경 감지)는 응용 프로그램에서 수동 트리거 또는 비동기 이벤트를 통해 변경이 발생하면 이를 자동으로 인식합니다. 변경이 감지되면 다양한 컴포넌트를 반복하고 새로 고침을 트리거합니다. 일반적으로 매우 빠르게 진행되지만 대용량의 응용 프로그램에서는 많은 컴퓨팅이 트리거되므로 메인 브라우저 스레드를 차단할 수도 있습니다.


다행히 Angular에서는 ChangeDetectionStrategy.OnPush를 통해 변경을 감지할 컴포넌트를 설정할 수 있습니다. 다음과 같은 경우를 제외하고 이 기능은 CD 중에 해당 컴포넌트를 건너뛰도록 Angular에 알려줍니다.

  • @Input() 참조가 변경되는 경우

  • 컴포넌트 또는 하위 컴포넌트 중 하나에서 이벤트가 발생한 경우

  • 컴포넌트에서 componentRef 또는 markForCheck()를 통해 CD가 명시적으로 실행되는 경우

  • 비동기 파이프가 보기에서 사용되는 경우


ChangeDetectionStrategy를 설정하는 방법은 간단하며 컴포넌트의 장식기 내에서 이루어집니다.

@Component({
  selector: 'app-mock',
  templateUrl: './mock.component.html',
  styleUrls: ['./mock.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})


이제는 모의 컴포넌트가 변경되는 동일한 페이지에서 다른 컴포넌트가 로드될 때마다 응용 프로그램이 작동하여 모의 컴포넌트를 제외하고 페이지에 로드된 모든 컴포넌트를 새로 고치게 됩니다. 모의 컴포넌트가 새로 고쳐지는 것은 모의 컴포넌트에서 무언가가 변경되거나 업데이트되는 경우뿐입니다.


FlexGrid 가상화로 DOM 요소 감소 


FlexGrid의 기본 목적은 JavaScript 개체를 사용자가 조작할 수 있는 DOM 요소로 변환하는 것입니다. 많은 인스턴스에서 이 데이터는 수백, 수천 또는 수백만 행의 데이터로 구성됩니다. 따라서 이러한 각 항목에 대해 DOM 요소를 생성할 경우 리소스를 많이 소모하게 되어 페이지 속도가 느리고 비대해질 수 있습니다.


가상화는 사용자에게 보이는 데이터 부분을 추적하고 해당 섹션만 DOM에서 렌더링하는 프로세스입니다. 이렇게 하면 특히 방대한 데이터 집합으로 작업할 때 문서 트리의 DOM 요소 수가 감소하고 성능이 훨씬 향상됩니다.


Wijmo는 viewRange 속성을 통해 데이터의 보이는 부분을 노출합니다. 사용자가 화면 크기를 변경하거나 그리드를 스크롤할 때마다 viewRange도 업데이트되어 DOM을 업데이트하게 됩니다. DOM의 요소 수가 증가하는 것을 방지하기 위해 FlexGrid는 viewRange에서 벗어나는 셀을 가져와 재활용함으로써 저장하고 있던 데이터에서 제거하고 viewRange로 들어오는 새 데이터로 다시 채웁니다. 이를 통해 DOM을 간단하게 만들고 응용 프로그램을 빠르고 가볍게 유지합니다.


이 샘플에서 관련 내용을 확인할 수 있습니다.

샘플


보다시피 현재 그리드에는 100개의 데이터 행이 있고 60개의 셀 요소가 DOM에 의해 렌더링되고 있습니다. 다음 코드를 사용하여 이 수치를 얻을 수 있습니다.

flexgrid.updatedView.addHandler((s, e) => {
  this.rowCount = s.rows.length.toString();
  this.cellCount = s.hostElement.querySelectorAll('.wj-cell').length.toString();
});


s.hostElement.querySelectorAll('.wj-cell') 메서드는 .wj-cell 클래스가 추가되어 DOM에서 렌더링된 요소의 배열을 반환합니다. 그리드를 아래로 스크롤하면 FlexGrid 내의 데이터 행 수는 늘어나고 셀 요소 수는 그대로인 것을 확인할 수 있습니다.

그리드


Angular 모듈 지연 로드

Angular이 응용 프로그램을 컴파일할 때 Webpack을 사용하여 만든 JavaScript 번들은 클라이언트에 전송됩니다.


 기본적으로 Angular는 클라이언트가 로드하는 페이지에 모듈 전체가 필요한 것이 아닌 경우에도 응용프로그램이 로드되자마자 모든 모듈을 로드합니다.


대용량의 응용 프로그램인 경우에는 응용 프로그램의 크기가 상당히 커집니다. 이는 번들 크기 역시 커질 것임을 의미합니다.


기본 번들의 크기가 커지면 다음과 같은 영향을 미치기 때문에 응용 프로그램의 성능이 저하됩니다.

  • 다운로드 속도 저하

  • 구문 분석 속도 저하

  • JavaScript 실행 속도 저하


많은 경로가 포함된 대용량의 응용 프로그램인 경우, 개발자는 필요에 따라 모듈을 로드하는 디자인 패턴인 지연 로드를 구현하는 것이 좋습니다. 지연 로드를 사용하면 초기 번들 크기를 더 작게 유지하는 데 도움이 되므로 로드 시간을 줄일 수 있습니다.


지연 로드 모듈을 구현하려면 라우팅할 컴포넌트가 포함된 기능 모듈이 필요할 것입니다. 새 NgModule을 생성하려면 터미널에서 다음 명령을 실행합니다.

ng generate module mock --route mock --module app.module


그러면 mock.module.ts 파일에서 정의된 지연 로드 가능한 기능 모듈 MockModulemock-routing.module.ts 파일에서 정의된 라우팅 모듈 MockRoutingModule이 있는 mock 폴더가 생성됩니다. 또한 이 명령은 앞에서 생성한 MockComponent를 자동으로 선언하고 MockRoutingModule을 새 기능 모듈 내부로 가져옵니다.


새 모듈은 지연 로드되므로 응용 프로그램의 app.module.ts 파일에 있는 새 기능 모듈에 대한 참조를 추가하지 않고, 대신 선언된 경로를 경로 배열에 추가합니다. 이제 app-routing.module.ts 파일은 다음과 같은 모양이 됩니다.

const routes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'mock', component: MockComponent, resolve: { mock: MockResolverService } },
  { path: 'mock', loadChildren: () => import('./mock/mock.module').then(m => m.MockModule) }
];


이제는 Mock 컴포넌트를 참조하는 경로가 2개입니다. 하지만 걱정하지 마세요! Angular는 확인자를 지연 로드와 함께 사용합니다. 따라서 우리가 할 일은 이 두 경로 개체를 하나로 결합하는 것입니다.

const routes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'mock', loadChildren: () => import('./mock/mock.module').then(m => m.MockModule), resolve: { mock: MockResolverService } }
];


응용 프로그램을 로드할 때 모의 모듈은 기본 번들과 함께 로드되지 않습니다. 대신에 응용 프로그램은 사용자가 mock 경로로 이동하여 이 모듈을 로드할 때까지 기다리므로 앱의 초기 로드 시간이 줄어들고 기본 번들 크기가 감소하게 됩니다.



결론


이제 이 모든 도구를 마음대로 사용함으로써 사용자가 응용 프로그램에서 작업할 때 Angular가 수행해야 할 작업량을 줄이면서 컴포넌트를 렌더링하고 제거할 때 브라우저의 DOM 부하를 줄일 수도 있습니다.





지금 바로 Wijmo를 다운로드하여 직접 테스트해보세요!

wj.png

  • 페이스북으로 공유
  • 트위터로  공유
  • 구글플러스로 공유
  • 카카오톡으로 보내기

댓글목록

등록된 댓글이 없습니다.

그레이프시티 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기

인기글

더보기
  • 인기 게시물이 없습니다.
그레이프시티 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기
이메일 : sales-kor@grapecity.com | 전화 : 1670-0583 | 경기도 안양시 동안구 시민대로 230, B-703(관양동, 아크로타워) 그레이프시티(주) 대표자 : 허경명 | 사업자등록번호 : 123-84-00981 | 통신판매업신고번호 : 2013-경기안양-00331 Copyright ⓒ 2022 GrapeCity inc.