해당 주제 선정 이유
회사 프로젝트를 진행하면서 1. HTTP 통신을 통해 다량의 데이터를 요청해야 하고, 2. 받은 데이터를 다양한 형태로 가공하고 계산해야 했다.
Web Worker를 이용하기 위해 찾아보았으나 한꺼번에 데이터를 사용해야 하는 서비스 특성상 Web Worker와 메인 스레드 간의 추가적 통신 시간이 우려되는 단점이 있어 결국 메인 스레드에서 동작시키게 되었다. 다만, Web Worker에 대해 궁금해지기도 했고 더 잘 이용할 수 있는 방법이 없을지 궁금해져 브라우저에서의 JS task 처리 과정과 Web Worker가 어떻게 메인 스레드 환경으로부터의 돌파구가 될 수 있는지에 대해 공부하고 정리하기로 했다.
⚠️ 해당 글에서는 웹 브라우저의 이벤트 루프와 스케줄링에 대해서만 다룹니다.
자바스크립트는 싱글 스레드 언어로 동작하기 때문에 동시성을 지원하지 않는다. 하지만, 실제로 웹을 이용해보면 동시에 여러가지 task를 처리해서 이를 체감하기 어려운데 이런 동작을 가능케 하는 것이 바로 Event Loop이다.
모든 task는 자바스크립트에서 실행되지 않고 일부를 브라우저에 위임하게 되며, 이 task는 자바스크립트 엔진이 아닌 브라우저 내부에서 비동기 및 논블로킹 방식*으로 처리되게 된다.
❓브라우저와 자바스크립트의 관계
브라우저는 곧 자바스크립트를 실행하는 소프트웨어로, 자바스크립트 엔진 구동 환경 중 하나이다. 결국 언어-실행 환경의 관계라고 생각하면 된다!
(ex. Java와 JVM)
☑️ 비동기, 그리고 논블로킹
메인 스레드가 작업을 다른 곳에 요청하여 대신 실행하고, 그 작업 완료 시 이벤트나 콜백 함수를 통해 결과를 실행하는 방식을 의미.
논블로킹은 다른 작업이 진행될 때도 자신의 작업을 진행하는 것을 의미한다. (반대는 블로킹으로 다른 작업 진행 중에 본인의 작업을 처리할 수 없는 것을 의미함.)
대부분의 자바스크립트 엔진은 크게 콜 스택과 힙으로 구성된다.
소스 코드(전역 코드, 함수 코드 등) 평가 과정에서 생성된 실행 컨텍스트가 추가되고 제거되는 스택 자료구조인 실행 컨텍스트 스택을 의미.
JS 엔진은 단 하나의 콜 스택을 가지므로, 최상위 실행 컨텍스트가 종료되어 제거되기 전까지는 다른 어떤 task도 실행되지 않음.
객체가 저장되는 메모리 공간으로 콜 스택 내부의 실행 컨텍스트가 힙에 저장된 객체를 참조.
객체는 크기가 정해져 있지 않아 할당 메모리 공간의 크기를 런타임에 결정, 즉 동적 할당함. 따라서 힙은 구조화되어 있지 않음!
근데, event loop가 위에서 말한 싱글 스레드인 거임?
No!!! 이벤트 루프는 자바스크립트 엔진이 아닌 브라우저 내장 기능이다. 위에서 언급한 두 가지 요소를 보면, 실행 컨텍스트는 콜 스택 하나에 의존해 실행되며 힙은 메모리 공간이므로 싱글 스레드가 의미하는 바는 결국 위의 콜 스택과 힙 동작 방식이라 볼 수 있다!
브라우저에 내장되어 있는 기능이며, task를 관리하는 역할을 한다. 위에서
모든 task는 자바스크립트에서 실행되지 않고 일부를 브라우저에 위임하게 되며, 이 task는 자바스크립트 엔진이 아닌 브라우저 내부의 멀티 스레드 Web APIs에서 비동기 및 논블로킹 방식으로 처리되게 된다.
라고 언급했는데, 브라우저에 위임하여 비동기 및 논블로킹 방식으로 처리할지 여부를 이 event loop가 관리하는 동시에 콜 스택의 완료 여부를 따져 대기 중인 실행 컨텍스트를 콜 스택으로 실행시키는 역할도 하게 된다.
콜 스택과 힙을 포함한 브라우저 환경의 구조는 아래와 같이 표현할 수 있다.
모던 자바스크립트 deep dive 내 이벤트 루프 구조도
브라우저에서 제공하는 API 모음들로 타이머, 네트워크 요청, 파일 입출력, 이벤트 처리 등을 포괄하여 말함. Web API의 경우 브라우저 내에서 멀티 스레드 환경으로 구현되어 있으므로 비동기 작업 처리가 가능.
Web API 종류
XMLHttpRequest, Timer API, console API, Canvas API, Geolocation API 등이 있다. MDN에서 전체 목록을 볼 수 있다!
https://developer.mozilla.org/ko/docs/Web/API
✍🏻 태스크 큐(task queue)
setTimeout이나 setInterval과 같은 비동기 함수의 콜백함수 혹은 이벤트 핸들러가 일시적으로 보관되는 장소. 이와 별개로 마이크로태스크 큐(microtask queue)도 존재하며, 마이크로태스크 큐는 프로미스 후속 처리 메서드의 콜백 함수를 일시적으로 보관한다.
태스크 큐 vs 마이크로태스크 큐
- 태스크 큐
마이크로태스크 큐와 구분하기 위해 매크로태스크 큐(macrotask queue)라고 작성하기도 한다.
setTImeout()
,setInterval()
,setImmediate()
와 같은 콜백 함수 혹은 이벤트 핸들러 보관- 마이크로태스크 큐
Promise
,async/await
,Object.observe
,MutationObserver
등과 같은 프로미스 후속 처리 메서드 콜백 함수 보관
Animation Frame Queue?
태스크 큐(매크로태스크 큐)와 마이크로태스크 큐 이외에 requestAnimationFrame과 같이 브라우저 렌더링과 관련된 task를 넘겨받는 Animation Frames도 존재한다.
보통 우선 순위는 MicroTask → Animation Frames → MacroTask 순이다!
이벤트 루프는 관리자 역할이므로 실제 작업을 실행하는 주체가 아니다. 또한 태스크 큐와 마이크로태스크 큐도 실행을 위해 대기하는 공간일 뿐, 실제 작업을 실행하는 주체는 콜 스택과 Web API이다!
웹 워커(Web worker)는 스크립트 연산을 웹 어플리케이션의 주 실행 스레드와 분리된 별도의 백그라운드 스레드에서 실행할 수 있는 기술입니다. 웹 워커를 통해 무거운 작업을 분리된 스레드에서 처리하면 주 스레드(보통 UI 스레드)가 멈추거나 느려지지 않고 동작할 수 있습니다.
참고로 Web Worker는 위에서 언급한 Web APIs 중 하나에 해당하기 때문에 멀티 스레딩으로 동작할 수 있는 것이다!
다음과 같은 사용 사례가 있을 수 있다.
Web Worker 사용 예제
- 화면 동작에 영향을 미치는 연산 동작 필요 시
(ex. 바이너리 파일 핸들링, 복잡한 연산)- 백그라운드 내 지속적인 작업 필요 시
- 메인 스레드와 분리된 작업 필요 시!
- 멀티스레드 환경을 통한 사용자 환경 개선 필요 시
특히 Web Worker에서 별도로 data fetching 과정을 수행할 수 있기 때문에, 백그라운드에서 대용량 데이터를 요청하는 작업이 필요한 등의 경우 SPA(Single Page Application)에서 사용하기에 유용하다!
postMessage API를 이용해 서로 메시지를 주고받는 것이 가능하다. onmessage는 이벤트에 해당한다.
postMessage()
주로 window 객체 간 통신에 이용되며, 그 예로는 팝업창 및 페이지 내 iframe의 데이터를 전달하는 사례가 있다.
HTTP 요청을 생성하지 않고 DOM 기반 통신에 이용된다.
// main.js
const worker = new Worker('./worker.js');
worker.postMessage([40, 2]);
// worker.js
addEventListener('message', event => {
const [a, b] = event.data;
// Do stuff with the message
// ...
});
기본적인 코드는 위의 형태로 동작한다. 다만, Web Worker의 사용방식이 정말 무궁무진하기 때문에, 각 상황에 맞춰서 사용하면 될 것 같다.
참고로, Vite 기반 React에서는 axios 사용도 가능하단다. 야호!
멀티스레딩을 지원하는 만큼, 현재 실행 환경의 CPU 코어 수에 따라 worker의 갯수를 달리하여 수행할 수도 있는 것이 Web Worker의 큰 장점이다.
Navigator.hadwareConcurrency를 활용하면 현재 환경의 프로세서 수를 가져올 수 있고, 이 프로세서 갯수만큼의 worker를 만들어 실행 시 더 빠르고 좋은 결과를 얻을 수 있다👍
참고 : React에서 웹 워커 인스턴스를 사용하여 이미지 처리를 수행한 사례
https://blog.rhostem.com/posts/2021-01-03-image-load-by-web-worker
모던 자바스크립트 deep dive(2023)
🔄-자바스크립트-이벤트-루프-구조-동작-원리
[JavaScript] Task Queue말고 다른 큐가 더 있다고? (MicroTask Queue, Animation Frames)
Callback queue, task queue, event loop 헷갈리는 개념 정리
Web Worker와 활용방법
웹 작업자를 사용하여 브라우저의 기본 스레드에서 자바스크립트 실행
MDN-Web Workers API