Web Worker

❗️ JS는 싱글 스레드 

JS는 싱글 스레드 기반으로 한 번에 한가지 작업만 처리할 수 있다.

for (let i=0; i<10000000; i++){}

위와 같은 코드를 브라우저 콘솔 창에 실행시킨다면 브라우저가 먹통이 되는 경험을 할 수 있다.

JS가 메인 스레드 하나만 가지기 때문이다.

오래 걸리는 코드를 다른 스레드에서 실행시키면 문제가 해결되는데 이때 web worker를 사용한다.


❗️ Web Worker?

싱글 스레드로 동작하는 JS의 단점을 보완하기 위해 나온 기능이다.

JS는 싱글 스레드로 동작하는데, 웹 브라우저는 멀티 스레딩을 지원한다.

worker는 JS가 제공하는 기능이 아니라 브라우저에서 제공하는 기능이다.

최신 브라우저는 대부분 web worker 기능을 지원한다.

 

web worker는 메인 스레드가 아닌 별도의 스레드인 worker 스레드에서 동작하는데,

worker 스레드는 백그라운드 스레드에서 실행된다.

 

UI는 메인 스레드에서 처리하고, 오래 걸리는 작업들은 worker 스레드에게 맡긴다.

메인 스레드와 worker 스레드는 서로 메시지를 주고 받으면서 소통한다.(message passing 방식)

따라서 서로의 메시지에 반응할 수 있도록 event handler를 등록해줘야 한다.


❗️ Web Worker 사용 방법

기존에는 index.js 파일에서 버튼을 누르면 for문을 10000000번 돌고 randomNumber를 표시해 주는 코드였다.

이제 for문을 worker 스레드에서 실행시키도록 해보자.

// index.js 파일

const button = document.getElementById('button');

button.addEventListener("click" , () => {
	for (let i=0; i<10000000; i++){};
    const randomNumber = Math.floor(Math.random() * 45) + 1;
	document.getElementById("result").innerHTML = randomNumber;
}

 

1. web worker 생성

worker.js 파일을 생성한 후, worker 파일 경로를 이용해서 worker 생성

// index.js 파일

const button = document.getElementById('button');

// worker 생성
const worker = new Worker("worker.js");

button.addEventListener("click" , () => {
	for (let i=0; i<10000000; i++) {};
    	const randomNumber = Math.floor(Math.random() * 45) + 1;
	document.getElementById("result").innerHTML = randomNumber;
}

 

2. worker에게 메시지 보내기

// index.js 파일

const button = document.getElementById('button');

// worker 생성
const worker = new Worker("worker.js");

button.addEventListener("click" , () => {
	// worker에게 메시지 보내기
	worker.postMessage('hi');
}

 

3. worker에서 for문 돌기

// worker.js 파일
onmessage = (event) => {
	for (let i=0; i<10000000; i++) {};
    	const randomNumber = Math.floor(Math.random() * 45) + 1;
}

onmessage는 메인 스레드로부터 전송된 메시지를 받을 수 있도록 해준다.

이제 for 문과 랜덤 번호 생성을 worker 스레드에서 처리하게 되었다.

 

4. 메인 스레드에 메세지 보내기

// worker.js 파일
onmessage = (event) => {
	for (let i=0; i<10000000; i++) {};
    	const randomNumber = Math.floor(Math.random() * 45) + 1;
    	postMessage(randomNumber);
}

하지만 web worker는 dom 요소에 직접 접근할 수 없다는 단점이 있다.

메인 스레드의 전역 변수는 window지만 worker 스레드의 전역 변수는 window가 아니기 때문이다.

 

따라서 작업이 끝난 후, 메인 스레드에게 randomNumber을 보내줘야 하는데

postMessage를 사용해서 메인 스레드에게 메시지를 보낼 수 있다.

 

이제 메인 스레드에도 worker의 메시지를 받을 수 있도록 설정해줘야 한다.

 

5. 메인 스레드에 onmessage 달기

// index.js 파일

const button = document.getElementById('button');

const worker = new Worker("worker.js");

// 메인스레드에 onmessage 달아줌
worker.onmessage = (event) => {
	const randomNumber = event.data;
  	document.getElementById("result").innerHTML = randomNumber;

}

button.addEventListener("click" , () => {
	worker.postMessage('hi');
});

worker에서 전달된 randomNumber를 화면에 렌더링 할 수 있게 되었다.

 

이제 브라우저는 randomNumber를 생성하는 동안 먹통이 되지 않고 다른 작업을 수행할 수 있다.


❗️ web worker error

만약 web worker에서 에러가 발생한다면 어떻게 될까?

web worker에서 발생한 에러는 메인 스레드까지 전파되지 않는다.

메인 스레드에서 web worker의 에러를 받고 싶다면 onerror 메서드를 이용하여 ErrorEvent를 받을 수 있다.

//index.js
worker.onerror = (error) => {
	console.error(error.message);
}

 

- worker 종료

만약 worker 스레드를 종료시키고 싶다면 아래 코드를 통해 종료시킬 수 있다.

worker.terminate();

❗️ worker 종류

  • Dedicated Worker
    Web Worker의 가장 일반적인 타입
    부모 자식 간의 스레드끼리만 메시지 교환이 가능
  • Shared Worker
    worker가 동일한 도메인 내에 존재하는 여러 스레드에서 사용 가능
    port를 이용해서 통신함
  • Service Worker
    웹앱, 브라우저, 네트워크 사이의 프록시 서버로써 작동
    PWA 구현 시, 사용됨
    도메인 당 하나만 등록 가능

 

 

⭐️ Web Worker 장점

  1. 향상된 성능 : 무거운 작업을 별도의 스레드에서 처리기 때문에 웹 어플리케이션의 응답성이 향상되어 사용자 경험 개선됨
  2. 병렬 처리 : 병렬 처리를 지원하므로 여러 작업을 동시에 실행할 수 있음
  3. 여러 개 등록 가능

 

⭐️ Shared Worker 장점

  1. 데이터 공유 : 동일한 오리진에서 생성되면 데이터를 공유할 수 있다.
  2. 생명주기 : 연결된 모든 컨텍스트가 종료되면 함께 종료된다.

 

⭐️ Service Worker 장점

  1. 리소스 캐싱 : 개발자가 로컬에 저장되는 리소스와 기간을 제어할 수 있음
  2. 오프라인 지원 : HTML, CSS, JS, 이미지와 같은 리소스를 캐싱할 수 있으므로 네트워크가 느리거나 연결되지 않은 경우에도 애플리케이션이 작동할 수 있음
  3. 푸시 알림 : 사용자의 브라우저에서 웹 어플리케이션이 열려 있지 않은 경우에도 알림을 보낼 수 있음
  4. 모든 탭이 닫혀있어도 백그라운드에서 동작할 수 있음

❗️ 비동기 vs 멀티 스레딩

비동기와 멀티 스레딩은 다른 개념이다.

비동기적이라는 것은 단일 스레드, 멀티 스레드 모두에게 적용될 수 있는 것이다.

 

비동기는 일(task)의 순서에 관한 것이고 멀티 스레드는 작업자(worker)이다.

멀티 스레딩은 여러 함수를 동시에 실행하고, 비동기는 여러 함수들이 non-blocking으로 실행된다.

 

비동기와 멀티 스레딩의 공통점은 작업이 끝나는 시점을 모른다는 것이다.


❗️ Web Worker vs Promise vs Web APIs

Promise는 싱글 스레드에서 동작한다.

하지만 이벤트 루프를 사용해서 비동기 작업을 처리하기 때문에

메인 스레드가 비동기 작업을 처리하는 동안 다른 작업을 할 수 있는 것이다.

 

Web APIs도 싱글 스레드에서 동작한다.

Web API는 비동기 작업을 이벤트 루프와 콜백 큐를 사용해서 비동기 작업을 처리한다.

비동기 작업은 백그라운드에서 처리하고, 작업이 완료되면 이벤트 큐에 콜백 함수를 추가한다.

setTimeout(() => {
	console.log('3초 대기 끝');
}, 3000);

위의 코드를 보면 3000ms가 지난 후, 이벤트 큐에 콜백 함수가 들어가고

콜백 함수는 메인 스레드가 비어 있을 때 실행된다.

 

Web Worker는 멀티 스레드로 실행된다.

메인 스레드와 별도로 실행되고, 메인 스레드와는 완전히 다른 스레드에서 작업을 한다.

 

Web worker는 브라우저에서 제공하는 기능으로, web apis의 일부이다.

반면에 Promise는 JS 언어의 기능이다.


🤔 그래서 자바스크립트는 싱글 스레드? 멀티 스레드?

web worker를 사용해서 멀티 스레딩을 일부분 구현할 수 있지만 기본 실행 모델은 아니다.

web worker의 스레드는 메인 스레드와 메모리를 공유하지 않기 때문에 전통적인 멀티 스레딩과는 차이가 있다.

 

=> 결론적으로 자바스크립트는 싱글 스레드지만 어느정도 멀티 스레딩이 가능하다~

 

 

'WEB' 카테고리의 다른 글

Web View  (2) 2024.10.01
SWC(Speedy Web Compiler)  (1) 2024.09.30
타입스크립트를 사용하는 이유  (0) 2024.09.06
Virtual Dom  (1) 2024.09.04
Polling/Long-Polling/SSE/Web Socket  (0) 2024.08.27