Web Worker 간단 정리하기

박성룡 ( Andrew park )
9 min readDec 22, 2019

--

Web Worker 에 대해서 간단하게 정리 해보고자 한다.

Web Worker 를 이해하기 위해서는 먼저 Modern Browser Thread 의 특성을 이해할 필요가 있다.

https://d2.naver.com/helloworld/2922312

Javascript 는 Single Thread 로 동작한다. 하지만 브라우저는 Single Thread 로 동작하지 않는다.

브라우저에서 처리되는 Network 통신이나 I/O 들은 서로 다른 Thread 에서 동작한다.

Web Worker 는 브라우저의 Main Thread 와 별개로 작동되는 Thread 를 생성할 수 있다.

Web Worker 로 생성한, Thread 는 브라우저 렌더링 같은 Main Thread 의 작업을 방해하지 않고, 새로운 Thread 에서 스크립트를 실행하는 간단한 방법을 제공한다.

Web Worker 간단 예제

다음은 Web Worker 를 만들 수 있는 간단한 예제이다.

Web Worker 가 실행할 수 있는 별도의 Javascript 파일이 필요하다.

// worker.jsvar n = 'done';
// 오래걸리는
// 무언가를
// 처리함...
postMessage(n);

그리고, 이를 생성할 Javascript 가 필요하다.

<body><!-- some --></body>
<script>
if( window.Worker ) {
var worker = new Worker('worker.js');
worker.onmessage = (event) => {
console.log(event.data);
// 'done'
worker.terminate();
};
}
</script>

예제과정

  1. main thread 에서 new Worker('worker.js') 로 worker thread 를 만든다.

2. main thread 에서 worker.onmessage 에 worker 의 메시지를 전달받기위한 이벤트 핸들러를 등록한다.

3. worker thread 에서 worker.js 의 작업 처리후 postMessage() 로 데이터를 main thread 의 worker.onmessage 에게 전달한다.

4. 만약 더이상 worker thread 가 필요 없거나, 작업을 종료해야 한다면, worker.terminate() 를 호출한다.

Web Worker Scope

Web Worker 은 main thread 와 별도의 worker thread 를 가진다.

main thread 에서는 window 가 GlobalScope 이다.

worker thread 는 main thread 의 window 와는 별도의 WorkerGlobalScope 를 가진다.

때문에 Window 의 메서드나 DOM 조작이 불가능하다.

self 를 이용해서, WorkerGlobalScope 에 접근이 가능하다.

self.onmessage = () => { }

worker thread 는 Message System 을 제외하면, main thread 의 window 에 직접 접근할 수 없고, DOM 에 접근하기 위해서는 반드시 worker thread 에서 main thread 에 Message System 로 데이터를 전달해야 한다.

데이터를 주고 받기 위해서, Message System (postMessage, onmessage) 을 이용하는데, 이때 전달하는 데이터는 복사하여, 새로운 값으로 만들어 전달한다.

만약 function 을 전달하면, 복사 할 수 없기 때문에, Error 가 발생되고, class 를 전달한다고 하더라도, Method 는복사되지 않는다.

class Test {
value = true;
onAction() {}
}
const test = new Test();
postMessage(test);
// { value: true }
test.value = false;
postMessage(test);
// { value: false }
postMessage(() => {});
// Uncaught DOMException

Web Worker Child Thread

worker thread 내에서 새로운 thread 를 생성 할 수 있다.

이때 worker thread 는 main thread 의 Origin 과 동일한데,동일한 Origin 을 가지는 javascript 를 가져올 수 있으며, 이를 가지고, 새로운 worker thread 를 만들 수 있다.

// worker.jsvar sub_worker = new Worker('sub_worker.js');
sub_worker.onmessage = function (event) {
console.log('worker', event.data);
}
sub_worker.postMessage('test');

postMessage 메서드를 이용해 부모 나 자식 Thread 에게 데이터를 전달할 수 있으며, onmessage 를 이용하여 부모나 자식 Thread 가 전달하는 데이터를 수신할 수 있다.

// sub_worker.jsonmessage = function (event) {
console.log('sub_worker', event.data);
postMessage(event.data);
}

Web Worker importScripts

window (main thread) 의 scope 에 접근할 수 없기 때문에, 만약 외부 라이브러리를 써야 한다면, importScripts 를 사용하여 다른 외부 스크립트를 가져올 수 있다.

importScripts 로 가져온 스크립트는 해당 worker thread 에만 영향을 주며 전역값 또한 worker thread 의 self (WorkerGlobalScope) 에 속하게 된다.

// test1.js
var test1 = 'test1';
// test2.js
var test2 = 'test2';
// worker.s
importScripts('test1.js', 'test2.js');
console.log(test1, test2);
importScripts('index.html');
// Refused to execute script

만약 MIME 타입이 Javascript 가 아닐 경우 Error 가 발생한다.

Web Worker Error

Worker Thread 내에서 Error 가 발생하면, Main Thread 까지 전파되지 않는다.

필요하다면, onerror 메서드를 이용하여 ErrorEvent 를 수신할 수 있다.

// err_worker.js
onmessage = (e) => {
console.log(e.data);
}
throw new Error('error-thread');

Error 보다 onmessage 가 먼저 등록 되었기 때문에, 위 예제의 메시지 수신이 가능하다.

var err_worker = new Worker('err_worker.js');
err_worker.onerror = (err) => {
// err: ErrorEvent { ... }
console.err(err.message);
}
err_worker.onmessageerror = (err) => {
// Uncaught DataCloneError: Failed to execute
console.err(err.message);
}
err_worker.postMessage(10);

onmessageerror 는 postMessage 로 데이터 전달 중 문제가 발생하면 호출된다.

Web Worker 종류

Web Worker 는 2가지 타입이 존재한다.

  • Dedicated Worker: 처음 생성 한 스크립트에서만 사용
  • Shared Worker: 여러 스크립트에서 사용

new Worker 로 생성한 Worker 는 Dedicated Worker 이며, 부모 자식 간의 Thread 끼리만 메시지 교환이 가능하다.

Shared Worker 는 Worker 가 동일한 도메인 내에 존재하는 여러 Thread 에서 사용이 가능하며, Port 를 이용해 통신 한다.

Shared Worker

SharedWorker 는 다른 컨텍스트 (tab, iframe, worker) 에서 접근이 가능한 워커다.

SharedWorker 는 SharedWorkerGlobalScope 를 가지며 port 를 이용해서, 데이터를 주고 받을 수 있다.

SharedWorkerGlobalScope 은 onconnect 로 접속한 port 에 연결한다.

// shared_worker.jsvar count = 0;
onconnect = function (e) {
var port = e.ports[0];
port.onmessage = function (e) {
port.postMessage(count++);
}
}

count 는 Shared Worker 가 더이상 다른 Thread 에서 참조하지 않을때 초기화 된다.

<body><!-- some --></body>
<script>
if (window.SharedWorker) {
var worker = new SharedWorker('shared_worker.js');
worker.port.postMessage();
worker.port.onmessage = function (e) {
console.log(e.data);
}
}

위 예제는 같은 url 로 접근하는, 서로 다른 탭에서 접근할때, count 값을 공유하는 것을 볼 수 있다.

--

--