! 제품 버전을 정확하게 입력해 주세요.
제품 버전이 정확하게 기재되어 있지 않은 경우,
최신 버전을 기준으로 안내 드리므로
더욱 빠르고 명확한 안내를 위해
제품 버전을 정확하게 입력해 주세요!

Web Worker로 Angular App 속도 향상하기 > 인사이트

본문 바로가기

MESCIUS 커뮤니티

인사이트

IT&개발 정보 Web Worker로 Angular App 속도 향상하기

페이지 정보

작성자 GrapeCity 작성일 2019-10-23 00:00 조회 7,666회 댓글 0건

본문

왜 웹 워커(Web Worker)가 필요합니까? 웹 워커는 웹 애플리케이션의 코드 컴포넌트입니다. 웹 워커를 통해 개발자는 JavaScript 작업에 대한 새로운 실행 스레드를 만들 수 있어 메인 앱의 실행을 방해하지 않습니다.


언뜻보기에 브라우저는 본질적으로 스레딩을 지원하고 개발자가 특별한 것을 할 필요가 없는 것처럼 보일 수 있지만, 불행히도, 그렇지 않습니다. 웹 워커는 실제 동시성 문제를 해결합니다.


웹 워커는 웹 브라우저의 예상되는 기능 표준의 일부이며 W3C 에서 사양이 작성되었습니다 Angular 프레임워크는 개발자들을 위해 웹 워커를 마무리했으며, CLI(Angular Command Line Interface)를 사용하여 웹 워커를 앱에 쉽게 추가 할 수 있습니다.


이 포스팅에서는 먼저 브라우저에서의 JavaScript와 스레드 동시성에 대한 몇 가지 오해를 살펴 보겠습니다. 그런 다음, 웹 사이트에서 동시 스레딩을 가능하게 하는 Angular로 웹 워커를 구현하는 것이 얼마나 쉬운지를 보여주는 기능적인 예제를 작성하겠습니다.


JavaScript는 본질적으로 동시적이지 않나요?


일부 개발자는 브라우저에서 JavaScript가 본질적으로 동시적이라고 생각합니다. 브라우저가 웹 사이트에 연결하고 페이지의 HTML을 검색 할 때, 브라우저가 다중 연결(약 6 개)을 열고 동시에 리소스(이미지, 링크 된 CSS 파일, 링크 된 JavaScript 파일 등)를 가져올 수 있기 때문입니다. 브라우저가 많은 스레드를 실행하고 동시에 컨텍스트 전환을 통해 수많은 작업을 실행하는 것처럼 보입니다.


이 때문에, 초보 웹 개발자는 브라우저가 동시 작업을 수행할 수 있다고 생각하게 됩니다. 그러나 JavaScript와 관련하여 브라우저는 실제로 한번에 하나의 프로세스만을 실행합니다.


대부분의 최신 웹 사이트, SPA (Single Page Apps) 및 최신 PWA (Progressive Web Apps)는 JavaScript에 의존하며 일반적으로 수많은 JavaScript 모듈을 포함합니다. 그러나 웹 앱에서 JavaScript를 실행하는 경우에 언제나 브라우저는 단일 활동 스레드로 제한됩니다. 정상적인 상황에서는 JavaScript가 동시에 실행되지 않습니다.


즉, JavaScript 모듈 중 하나에 장기간 실행되거나 프로세스 집약적인 작업이 정의되어 있는 경우 브라우저가 사용자 인터페이스(UI)를 업데이트하기 전에 프로세스가 완료될 때까지 기다리는 동안 앱이 멈추거나 멈추는 것처럼 보일 수 있습니다. 이러한 동작은 사용자들로 하여금 웹 앱이나 SPA에 대한 자신감을 잃게 만듭니다.


JavaScript 동시성에 대한 웹 워커


웹 앱에서 동시 JavaScript 스레드를 실행할 수 있도록 두 개의 분할창이 있는 예제 페이지를 작성해 보겠습니다. 첫번째 창에서 애플리케이션의 UI는 그림이 지속적으로 업데이트되고 마우스 클릭에 반응하는 원이있는 격자로 표시됩니다. 두 번째 창은 일반적으로 UI 스레드를 차단하고 UI가 작업을 수행하지 못하게 하는 장기 실행 프로세스를 호스팅합니다.



 


UI를 반응형으로 만들기 위해, 별도의 스레드에서 UI 논리 실행을 차단하지 않는 웹 워커에서 긴 프로세스를 실행할 것입니다. Angular는 웹 워커 구성을 간단한 단일 명령 작업으로 만들기 때문에 앱 빌드를 위한 프레임 워크로 사용합니다.


Angular 앱 설정


Angular CLI를 사용하려면 Node.js 및 NPM (Node Package Manager)이 설치되어 있어야 합니다. Node와 NPM이 설치되었는지 확인한 후 콘솔 창을 연 다음 NPM을 통해 Angular CLI를 설치합니다. (일회성 실행)


npm install -g @angular/cli 


새로운 앱 템플릿을 만들고자 하는 대상 디렉토리로 디렉토리를 변경합니다. 이제 앱을 만들 준비가 되었습니다. 이를 위해, “ng new”명령을 사용합니다. 프로젝트 이름을 NgWebWorker로 지정합니다.


ng new NgWebWorker 


프로젝트 마법사는 프로젝트에 라우팅을 포함할지 묻습니다. 이 예제에서는 라우팅이 필요하지 않으므로 n을 입력합니다.


그런 다음, 사용하려는 스타일 시트 형식 유형을 묻습니다. Angular는 Sass 및 Less와 같은 스타일 시트 프로세서 사용을 지원하지만, 우리는 간단한 CSS를 사용할 것이므로 기본값을 입력하기 위해 Enter를 누릅니다.



 


그러면 NPM이 필요한 패키지를 가져오고 CLI가 템플릿 프로젝트를 생성 할 때 일부 CREATE 메시지가 표시됩니다. 마지막으로 명령 행에서 커서가 다시 깜박입니다.


이 시점에서 Angular CLI는 프로젝트를 생성하여 NgWebWorker 폴더에 배치했습니다. NgWebWorker로 디렉토리를 변경하세요. Angular CLI는 Node HTTP 서버 설치를 포함하여 Angular 프로젝트에서 작업하는 데 필요한 모든 것을 만들었습니다. 즉, 템플릿 앱을 시작하기 위해 해야 ​​할 일은 다음과 같습니다.


ng serve 



 


Angular CLI는 프로젝트를 컴파일하고 노드 HTTP 서버를 시작합니다. 이제 <a href=" http://localhost:4200"target="_blank"> URL 을 가리키면 브라우저에 앱을 로드 할 수 있습니다.


CLI의 첫 번째 Angular 앱


페이지를 로드하면 맨 위에 프로젝트 이름이 있는 기본 템플릿이 표시됩니다.


"ng serve" 명령어를 실행하고 코드를 변경하면 브라우저에서 사이트가 자동으로 새로 고침 됩니다. 변경 사항이 적용되는 것을 훨씬 쉽게 볼 수 있습니다.


우리가 집중할 코드의 대부분은 / src / app 디렉토리에 있습니다.



 


app.component.html에는 현재 기본 페이지를 표시하는 데 사용되는 하나의 컴포넌트에 대한 HTML이 포함되어 있습니다. 컴포넌트 코드는 app.component.ts (TypeScript) 파일에 표시됩니다.


app.component.html의 내용을 삭제하고 자체 레이아웃을 추가하겠습니다. 왼쪽에 장기 실행 프로세스 값을 표시하고 오른쪽에 임의의 원을 그리는 분할 페이지를 만듭니다. 그러면 브라우저가 독립적으로 작동하는 2개의 추가 웹 워커 스레드를 실행하여 Angular 웹 워커가 작동하는 것을 볼 수 있습니다.


이 포스팅의 나머지 부분에 대한 모든 코드는 GitHub 레포지토리 에서 얻을 수 있습니다.


최종 페이지가 임의의 원을 그리는 동안의 모습은 다음과 같습니다. (장기 프로세스가 시작되기 전).



 


app.component.html의 코드를 다음으로 바꿉니다.


<div id="first">

<div class="innerContainer"><button (click)="longLoop()">Start Long Process</button></div>

<div class="innerContainer"><textarea rows="20" [ngmodel]="longProcessOutput"></textarea></div>

</div>


코드 다운로드에는 일부 ID 및 CSS 클래스와 styles.css의 관련 스타일이 포함되어 있으며 UI의 매우 간단한 서식에 사용되므로 두 섹션 (왼쪽 및 오른쪽)과 기타 기본 스타일이 있습니다.


여기서 주목해야 할 점은 버튼에 Angular 이벤트 바인딩(클릭)을 추가하여 사용자가 버튼을 클릭할 때 컴포넌트 TypeScript 파일인 app.component.ts에 있는 longLoop 메소드를 호출하여 장기 프로세스가 시작된다는 것입니다.


longLoop(){
   this.longProcessOutput = "";
   for (var x = 1; x <=1000000000;x++){
     var y = x/3.2;
     if ((x % 20000000) == 0){
        this.longProcessOutput += x + "\n";
        console.log(x);
} 


이렇게 하면 컴포넌트인 longProcessOutput의 멤버 변수에 100억 개의 반복 쓰기 작업이 실행됩니다.

 

app.component.html (textarea 요소)에 해당 멤버 변수를 바인딩했기 때문에 변수가 업데이트 될 때마다 UI에 업데이트가 반영됩니다. HTML에서 설정한 ngModel 값은 멤버 변수를 바인딩하는 곳입니다.


 <textarea rows="20" [ngmodel]="longProcessOutput"></textarea>


이제 실행해보세요. 버튼을 클릭하면 아무 일도 일어나지 않으며 갑자기 TextArea가 많은 값으로 업데이트됩니다. 콘솔을 열면 코드가 실행될 때 작성된 값을 볼 수 있습니다.


Angular CLI를 사용하여 임의의 원 컴포넌트 추가


다음으로 임의의 원을 그리는 “circle” 컴포넌트를 추가합니다. 아래의 명령으로 Angular CLI를 사용하여 추가할 수 있습니다.


 ng generate component circle


이 명령은 circle이라는 새 폴더를 만들고 4 개의 새 파일을 만듭니다.


  • circle.component.html
  • circle.component.spec.ts (단위 테스트)
  • circle.component.ts (TypeScript 코드)
  • circle.component.css (이 컴포넌트와 연관된 HTML에만 적용되는 스타일)





    HTML은 매우 간단합니다. 
    컴포넌트를 나타내는 HTML 만 있으면 됩니다. 이 경우 페이지 오른쪽의 컴포넌트로 연한 녹색 격자가 표시되고 원이 그려집니다. 이 작업은 HTML Canvas 요소를 통해 수행됩니다.
     

<div id="second">
   <canvas #mainCanvas (mousedown)="toggleTimer()"></canvas>
</div>


우리는 mousedown 이벤트를 잡기 위해 Angular 짝수 바인딩을 추가하여 원 그리기를 시작하고 중지합니다. 즉, 사용자가 캔버스 영역 내부를 클릭하면 프로세스가 아직 시작되지 않은 경우, 원을 그리기 시작합니다. 프로세스가 이미 시작된 경우 toggleTimer 메서드 (circle.component.ts에 있음)는 그리드를 지우고 원 그리기를 중지합니다.


toggleTimer는 단순히 setInterval을 사용하여 100 밀리 초마다 무작위로 선택한 색상으로 원을 그립니다.


toggleTimer(){
   if (CircleComponent.IntervalHandle === null){
     CircleComponent.IntervalHandle = setInterval(this.drawRandomCircles,50);
   }
   else{
     clearInterval(CircleComponent.IntervalHandle);
     CircleComponent.IntervalHandle = null;
     this.drawGrid();
   }
 }


Circle.component.ts에는 Canvas 요소를 설정하고 멤버 변수를 초기화하고 드로잉을 수행하는 더 많은 코드가 있지만 지금은 이 정도까지만 다루겠습니다.


페이지가 로드되면 원이 그려지기 시작합니다. [Start Long Process] 버튼을 클릭하면 도면이 일시 정지됩니다. 모든 작업이 동일한 스레드에서 수행되기 때문입니다.


웹 워커를 추가하여 이 문제를 해결해 보겠습니다.


Angular 웹 워커 추가


CLI를 사용하여 새 웹 워커를 추가하려면 프로젝트 폴더로 이동하여 다음 명령을 실행하세요.


ng generate web-worker app 


마지막 매개 변수(app)는 웹 워커에 배치하려는 장기 실행 프로세스를 포함하는 컴포넌트의 이름입니다.



 


Angular는 app.component.ts에 다음과 같은 코드를 추가합니다.


if (typeof Worker !== 'undefined') {
 // Create a new
 const worker = new Worker('./app.worker', { type: 'module' });
 worker.onmessage = ({ data }) => {
   console.log(`page got message: ${data}`);
 };
 worker.postMessage('hello');
} else {
 // Web Workers are not supported in this environment.
 // You should add a fallback so that your program still executes correctly.
}


이 새로운 코드가 명령이 추가한 새로운 app.worker 컴포넌트를 참조한다는 것을 알 수 있습니다. 여기서, 코드는


  1. 브라우저가 웹 워커를 지원하는지 확인합니다.
  2. 새로운 워커를 만듭니다.
  3. 워커에게 메시지를 게시합니다. (app.worker.ts에 있음)
  4. 워커가 "hello" 메시지를 받으면 EventListener가 시작됩니다. (다음 코드 스니펫에서 확인하실 수 있습니다.)
  5. app.worker.ts에서 EventListener가 실행되면 response 객체가 생성되어 호출자에게 다시 게시됩니다.
     

app.worker.ts의 전체 내용은 다음과 같습니다.


addEventListener('message', ({ data }) => {
 const response = `worker response to ${data}`;
 postMessage(response);
});


이러한 과정을 거치면, 콘솔에 메시지가 표시되며, 아래 이미지에서 볼 수 있는 콘솔 출력의 마지막 줄과 같습니다.



 


이것이 원래 Worker 객체에서 생성된 EventHandler에서 발생하는 console.log입니다.


worker.onmessage = ({ data }) => {
   console.log(`page got message: ${data}`);
 };


이를 통해, app.component가 app.worker에 메시지를 게시하고 app.worker가 자체 메시지로 응답했음을 알 수 있습니다.


Worker를 사용하여 다른 스레드에서 장기 실행 프로세스를 실행하여 원 그리기 코드가 중단되지 않도록 합니다.


먼저, UI 요소와 관련된 코드를 app.component 클래스의 생성자로 옮깁니다.


constructor(){
   if (typeof Worker !== 'undefined') {
     // Create a new
     const worker = new Worker('./app.worker', { type: 'module' });
     worker.onmessage = ({ data }) => {
       this.longProcessOutput += `page got message: ${data}` + "\n";
     };
     worker.postMessage('hello');
   } else {
     // Web Workers are not supported in this environment.
     // You should add a fallback so that your program still executes correctly.
   }


이제 longProcessOutput 변수를 참조할 수 있습니다. 이 변수에 접근 할 수 있게 되었으므로 worker.onmessage는 초기 테스트처럼 콘솔에 쓰는 대신 기본 데이터를 TextArea에 추가합니다.


왼쪽에 강조 표시된 텍스트가 수신 된 메시지임을 볼 수 있습니다.



 


웹 워커의 LongLoop


루프가 실행될 때 자체 스레드에서 실행되도록 장기 실행 루프를 웹 워커로 이동해야 합니다.


최종 app.component.ts에서 사용할 코드는 다음과 같습니다.


 constructor(){
   if (typeof Worker !== 'undefined') {
     this.worker = new Worker('./app.worker', { type: 'module' });;
     this.worker.onmessage = ({ data }) => {
       this.longProcessOutput += `${data}` + "\n";
     };
   } else {
     console.log("Web Workers are not supported by your browser");
   }
 }

 longLoop(){
   this.longProcessOutput = "";
   // the following line starts the long process on the Web Worker
   // by sending a message to the Web Worker
   this.worker.postMessage("start looping...");
 }


Worker 변수를 클래스로 옮겼으므로 이제 멤버 변수가 되었습니다. 그렇게 하면 AppComponent 클래스의 어느 곳에서나 쉽게 참조할 수 있습니다.


다음으로 생성자의 코드를 사용하여 Worker 객체에 메시지 이벤트 핸들러를 정의한 방법에 대해 자세히 살펴 보겠습니다.


this.worker.onmessage = ({ data }) => {
       this.longProcessOutput += `${data}` + "\n";
     };


이 코드는 웹 워커 클래스 (app.worker.ts에 있음)가 postMessage (data)를 호출할 때 실행됩니다. postMessage 메소드가 호출 될 때마다 longProcessOutput (TextArea에 바인드 된 모델)은 데이터와 함께 캐리지 리턴 ( "\ n")으로 업데이트됩니다. 간단히 말하자면, 각 값이 TextArea 요소의 자체 줄에 기록됩니다.


실제 웹 워커(app.worker.ts)에 있는 전체 코드는 다음과 같습니다.


addEventListener('message', ({ data }) => {
 console.log(`in worker EventListener : ${data}`);
 for (var x = 1; x <=1000000000;x++){
   var y = x/3.2;
   if ((x % 20000000) == 0){
     // posts the value back to our worker.onmessage handler
      postMessage(x);
      // don't need console any more --> console.log(x);
   }
 }
}); 


사용자가 [Run Long Process] 버튼을 클릭하면 시작되는 이벤트 핸들러입니다. 웹 워커 (app.worker.ts)에 있는 모든 코드는 새 스레드에서 실행됩니다. 이것이 웹 워커의 가치입니다. 코드는 별도의 스레드에서 실행됩니다. 그렇기 때문에 더 이상 웹앱의 메인 스레드에 영향을 미치지 않습니다.


longLoop 메소드에 다음 코드가 있으므로 사용자가 버튼을 클릭하면 웹 워커 코드가 시작됩니다.


longLoop(){
   this.longProcessOutput = "";
   // the following line starts the long process on the Web Worker
   // by sending a message to the Web Worker
   this.worker.postMessage("start looping...");
 }


메시지가 워커에 게시되면 EventListener가 시작되어 원래 longLoop에서 코드를 실행합니다.


웹 워커로 Angular 앱 마무리


이제 앱을 실행할 때 [Start Long Process] 버튼을 클릭해도 더 이상 원 그리기가 일시 중지되지 않습니다. longLoop가 계속 실행되는 동안 클릭하면 캔버스가 즉시 다시 그려지도록 원 그리기 캔버스 구성 요소와 직접 상호 작용할 수도 있습니다. 이전에는 이 ​​작업을 수행하면 앱이 정지된 것처럼 동작했습니다.


이제 Angular Web Worker에 장기 실행 프로세스를 추가하여, 더 이상 앱이 멈추지 않아 편하게 사용할 수 있습니다.


JavaScript Web Worker로 해결된 예제를 Plunker 에서 확인하실 수 있습니다.
 


프레임워크에 구애받지 않는 웹 컴포넌트를 찾고 계신가요?


- Wijmo : JavaScript 기반 Chart, Grid, Input, UI 컨트롤 

- SpreadJS : JavaScript 기반 MS Excel 스프레트 시트

  • 페이스북으로 공유
  • 트위터로  공유
  • 링크 복사
  • 카카오톡으로 보내기

댓글목록

등록된 댓글이 없습니다.

메시어스 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기

인기글

더보기
  • 인기 게시물이 없습니다.
메시어스 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기
이메일 : sales-kor@mescius.com | 전화 : 1670-0583 | 경기도 과천시 과천대로 7길 33, 디테크타워 B동 1107호 메시어스(주) 대표자 : 허경명 | 사업자등록번호 : 123-84-00981 | 통신판매업신고번호 : 2013-경기안양-00331 ⓒ 2024 MESCIUS inc. All rights reserved.