웹 워커(Web Worker)란?

Khan·2025년 2월 4일
0
❗자바스크립트는 놀랍게도 싱글 스레드이다.

싱글 스레드의 한계

싱글 스레드의 경우 기본적으로 연산량이 많은 작업을 하는 경우, 그 작업이 완료되고 다른 작업을 수행할 수 있다.

setInterval과 같은 함수로 반복 로직을 구현한 경우 브라우저의 정책으로 인해 쓰로틀링이 걸려버릴 수 있습니다.

예를 들어 브라우저 탭이 비활성화 될 경우 크롬의 경우 시간이 멈추고, 사파리는 시간이 1.5배속으로 흐르는 등... 결과가 전혀 보장되지 않을 수 있다.

크롬에서 타이머를 켜두고 다른 탭에서 일을 보다가 다시 타이머를 켜둔 탭으로 돌아와서 봤는데 시간이 이렇게 차이가 난다.

쓰로틀링이란?

쓰로틀링이란 브라우저가 자체적으로 판단했을 때 비활성화 된 탭에서 반복적으로 발생하는 이벤트가 있다면 그 이벤트에 delay등을 줘서 결국 해당 이벤트를 무시하고 메모리 자원을 다른 곳에 쓰는 등 메모리를 효율적으로 관리하는 방법이다. 어떻게 보면 브라우저의 입장에서는 이해가 되는 부분이지만 로직을 짜는 입장에서는 답답할 수 밖에 없다. 정말 계속해서 몇초마다 돌아야 하는 기능을 구현을 하게 된다면 이는 큰 문제가 될 것이다.

Web Worker

Web Worker 란?

  • 웹 워커는 자바스크립트의 메인 스레드가 아닌 브라우저의 백그라운드 스레드에서 돌기 때문에 브라우저 탭이 비활성화 되어도 영향을 받지 않고 멀티 스레딩의 장점을 취할 수 있다.

Web Worker와 관련한 간단한 워크플로우

왼쪽은 Web Worker를 사용하지 않은 상태이고, 오른쪽은 Web Worker를 사용한 상태이다.

자바스크립트는 브라우저에서 싱글 스레드 기반으로 동작한다.
따라서, Web Worker를 사용하지 않았을 때에는 UI 동작과 데이터 핸들링이 순차적인 방식으로 처리가 되고,
Web Worker를 사용하면 병렬 처리가 가능하다.

자바스크립트 엔진 동작 방식

왼쪽에 태스크 큐, 마이크로 태스크 큐, 애니메이션 프레임이 있다.

일반적인 이벤트 처리를 담당하는 태스크 큐, Promise와 같은 특별한 비동기 처리를 담당하는 마이크로태스크 큐, 그리고 부드러운 화면 전환을 위한 애니메이션 프레임 큐가 있다. 각각의 큐는 자신만의 고유한 역할과 우선순위를 가지고 있다.

이벤트 루프는 이러한 큐들과 콜 스택을 지속적으로 감시하면서 작업을 조율한다. 콜 스택이 비어있을 때, 가장 먼저 마이크로태스크 큐의 모든 작업을 처리하고, 그 다음으로 태스크 큐의 작업을 하나 처리하며, 마지막으로 애니메이션 프레임의 작업을 처리한다

사용자가 직접 데이터를 처리하는 부분은 싱글 스레드에서 동작되므로 데이터 처리가 많아질수록 병목현상이 생겨 렌더링 프레임에 영향을 미치게 된다.

병목현상을 처리하기 위해서 Web Worker를 사용하게 된다.

메인 스레드는 우리가 일반적으로 사용하는 자바스크립트가 실행되는 공간으로, 웹 페이지의 UI를 담당한다.

워커 스레드들은 메인 스레드를 방해하지 않고 복잡한 계산이나 데이터 처리 같은 무거운 작업을 처리할 수 있다.

메인 스레드와 워커 스레드는 'postMessage'라는 방식으로 서로 통신한다. 직접적인 메모리 공유 대신 메시지 전달 방식을 사용함으로써 각 스레드의 독립성을 보장하고 동시에 안전한 데이터 교환을 가능하게 한다.
메시지가 전달되면 각 스레드의 메시지 큐에 저장되고, 해당 스레드의 이벤트 루프가 이를 처리하게 된다.

Web Worker를 사용하지 않는 이유

  1. 서버 중심의 데이터 처리 구조
    • 현대 웹 개발에서는 대부분의 복잡한 연산과 데이터 처리를 서버 측에서 담당하고 있다. 클라이언트 측에서는 주로 데이터를 표시하고 사용자 인터페이스를 관리하는 정도의 작업만 수행하기 때문에, 웹 워커를 사용할 만큼 무거운 작업을 하는 경우가 매우 드물다.
  2. 기술적 제약사항이 존재
    • 웹 워커에서는 DOM 조작이나 Window 객체의 일부 함수를 사용할 수 없다. 이는 웹 개발에서 가장 기본적이고 필수적인 기능들이 제한된다는 것을 의미하며, 이로 인해 웹 워커의 활용 범위가 상당히 좁아지게 된다
  3. 성능상의 이슈
    • 단순한 계산의 경우에는 오히려 메인 스레드에서 직접 처리하는 것이 더 빠를 수 있다. 또한 메인 스레드와 웹 워커 사이에서 데이터를 주고받을 때마다 발생하는 통신 비용은 전체적인 성능에 부정적인 영향을 미칠 수 있다.

Web Worker를 사용하는 경우

  1. 복잡한 연산 작업을 처리할 때 유용
    • 바이너리 파일 처리나 복잡한 수학적 계산과 같은 작업을 웹 워커에서 처리하면 메인 스레드의 부하가 줄어든다. 이를 통해 사용자 인터페이스의 반응성을 유지하면서 무거운 작업을 수행할 수 있다.
  2. 지속적인 백그라운드 작업이 필요한 경우에 적합
    • 주기적인 데이터 동기화나 실시간 데이터 처리와 같이 메인 스레드와 독립적으로 실행되어야 하는 작업들을 웹 워커로 효과적으로 처리할 수 있다.
  3. 싱글 페이지 애플리케이션(SPA)과 같은 복잡한 웹 애플리케이션의 사용자 경험을 개선할 수 있음
    • 멀티스레드 환경을 활용해 애플리케이션의 전반적인 성능이 향상되며, 데이터 처리와 화면 렌더링을 병렬로 수행해 더 부드러운 사용자 경험을 제공한다.

예시

/* main.js */

// 웹 워커 생성
const worker = new Worker('worker.js');

// 워커로 메시지 전송
worker.postMessage({
  number: 100000000
});

// 워커로부터 메시지 수신
worker.onmessage = function(e) {
  console.log('결과 값:', e.data);
  worker.terminate(); // 워커 종료
};
  • 메인 스레드에서는:
    • Worker 객체를 생성한다.
    • postMessage를 통해 워커에게 계산할 숫자를 전달한다.
    • onmessage를 통해 워커의 계산 결과를 받는다.
    • 작업이 완료되면 terminate()로 워커를 종료한다.
/* worker.js */

// 메인 스레드로부터 메시지 수신
self.onmessage = function(e) {
  const number = e.data.number;
  
  // 시간이 오래 걸리는 작업 예시 (1부터 주어진 숫자까지의 합)
  let sum = 0;
  for(let i = 1; i <= number; i++) {
    sum += i;
  }
  
  // 결과를 메인 스레드로 전송
  self.postMessage(sum);
};
  • 워커 스레드에서는:
    • onmessage를 통해 메인 스레드의 메시지를 받는다.
    • 전달받은 숫자로 계산을 수행한다.
    • postMessage를 통해 계산 결과를 메인 스레드로 전달한다.
profile
끄적끄적 🖋️

0개의 댓글