web
기존 자바스크립트는 Single Thread(싱글 스레드)로 작동합니다. 그렇기 때문에 복잡한 계산이나 로딩이 오래 걸리는 작업이 메인 스레드에서 실행되면, 페이지가 응답 불가 상태(“멈춘” 상태)가 될 수 있습니다. 이러한 문제를 해결하기 위해 Web Worker라는 스레드 API가 등장했습니다. Web Worker를 활용하면 메인 스레드와 독립적으로 동작하는 Multi Thread(다중 스레드)를 자바스크립트에서 생성하고 제어할 수 있습니다.
Web Worker는 웹 애플리케이션이 무거운 연산이나 비동기 작업을 별도의 스레드에서 처리하도록 지원하는 자바스크립트 API입니다. 이 API를 사용하면 브라우저의 메인 스레드(Main Thread)와 분리된 스레드에서 특정 로직을 수행할 수 있으므로, 메인 스레드의 렌더링이나 사용자 인터페이스(UI) 반응성이 떨어지지 않도록 할 수 있습니다.
예를 들어, 일반적인 HTML <script> 태그에서 무거운 루프나 대규모 계산을 수행하면, 해당 작업이 끝날 때까지 화면 렌더링이나 이벤트 처리가 멈추는 현상이 생길 수 있습니다. 하지만 Web Worker를 사용하면, 이런 무거운 작업을 메인 스레드와 분리하여 동시에(또는 병렬적으로) 진행할 수 있고, 따라서 브라우저 UI는 원활하게 움직이게 됩니다.
Web Worker는 독립적인 파일(또는 Blob URL)을 통해 정의됩니다. 브라우저는 이 파일을 별도의 스레드로 로드하여 실행합니다. 그리고 postMessage / onmessage(또는 addEventListener('message', ...)) 구문으로 서로 데이터를 주고받을 수 있습니다. 메인 스크립트(Main Thread)와 Web Worker 스레드는 동기가 아닌 비동기 방식으로 메시지를 교환하며, 데이터 복사나 직렬화가 이루어지므로, 복잡한 객체를 주고받을 때 성능 고려가 필요할 수 있습니다. Web Worker는 DOM에 직접 접근할 수 없으며, 오직 네트워크 요청, 타이머, postMessage 등 제한된 API만 사용할 수 있습니다.
브라우저 Web Worker에서는 전역 객체가 self이고 self.onmessage/postMessage를 썼지만, Node.js Worker에서는 require('worker_threads')를 통해 parentPort.on('message')와 parentPort.postMessage()를 사용합니다.
무거운 연산(예: 큰 수의 합산)을 Web Worker에서 처리하고, 결과만 메인 스레드로 전달해보는 예시입니다.
// worker.js
self.onmessage = function (e) {
console.log('Message from main script:', e.data);
// 예) 큰 범위의 합을 구하는 무거운 작업
let sum = 0;
for (let i = 0; i < 5e7; i++) {
sum += i;
}
// 계산 완료 후 결과를 메인 스레드에 전달
postMessage(sum);
};
위의 코드는 Web Worker 스크립트에서 사용하는 전형적인 형태이며, 자바스크립트 문법상으로도 올바른 코드입니다. 다만 주의해야 할 점은, Web Worker 환경에서는 self가 전역 객체 역할을 하므로(브라우저의 window 객체와 대응되는 개념), 웹 워커 내부에서 이벤트 리스너를 등록할 때 self.onmessage를 사용하게 됩니다.
// main.js
// Web Worker 인스턴스 생성
const worker = new Worker('worker.js');
// 웹 워커에서 메시지를 받으면 실행되는 콜백
worker.onmessage = function (e) {
console.log('Result from worker:', e.data);
};
// 웹 워커에 메시지(작업 시작 신호)를 보냄
worker.postMessage('Start calculation');
위 예시에서는 worker.js를 별도의 파일로 만들어서, 루프를 활용한 큰 수의 합산 작업을 수행하도록 했습니다. 메인 스레드는 worker.onmessage를 통해 결과가 도착할 때까지 비동기로 대기하고, 그 동안 다른 UI 처리를 멈추지 않고 계속할 수 있습니다.
가장 큰 장점은 **메인 스레드(Main Thread)**가 연산에 묶여 UI가 멈추는 현상을 방지할 수 있다는 점입니다. 일반적으로 브라우저는 자바스크립트 코드를 단일 스레드로 실행하므로, 하나의 스레드가 복잡한 연산을 계속 수행하면 그동안 사용자 이벤트 처리나 화면 갱신이 차단될 수 있습니다. 하지만 Web Worker를 통해 해당 로직을 독립된 스레드에서 실행하도록 하면, 메인 스레드는 UI 렌더링과 이벤트 처리를 계속할 수 있어 사용자 경험(UX)이 크게 개선됩니다.
또한, CPU 코어가 여러 개인 환경에서는 실질적인 “병렬 처리”에 가까운 효과를 기대할 수 있습니다. 예컨대 8코어 CPU가 있는 사용자의 브라우저에서 무거운 연산을 하나의 코어에 할당해놓고, 나머지 코어들이 메인 스레드나 다른 작업을 처리하면 전체적인 처리량이 늘어날 수 있습니다. 물론 실제 병렬화 수준은 운영체제와 브라우저 엔진의 스케줄링에 따라 달라지지만, 적어도 단일 스레드에 모든 일을 몰아 넣는 것보다는 훨씬 유연한 구성이 가능해지는 것이 사실입니다.
Web Worker는 UI를 담당하는 메인 스레드와 완전히 분리된 환경에서 동작하기 때문에, DOM 객체에 직접 접근하거나 조작할 수 없습니다. 즉, HTML 요소를 선택하거나 스타일을 변경하려면 여전히 메인 스레드에서 처리해야 하며, 워커 측에서는 그저 계산 혹은 네트워크 요청 등의 로직만 담당할 수 있습니다. 이 점은 “UI가 끊김 없이 동작하도록 하는” 목적에는 부합하지만, 만약 워커에서 DOM 변경이 필요해진다면 메인 스레드와 메시지로 협업하는 방식을 설계해야 합니다.
또 한 가지 주의해야 할 점은 메시지를 통해 데이터를 주고받을 때 발생하는 직렬화(Serialization) 비용입니다. Web Worker와 메인 스레드는 별도의 스레드이므로, 객체나 배열 등을 주고받을 때 시스템 내부적으로 복사 과정이 일어나게 됩니다. 따라서 데이터가 클수록 전송 시간이 길어지고, 직렬화·역직렬화 오버헤드가 커져 성능 저하를 일으킬 수 있습니다. 큰 이미지를 통째로 보내거나, 수십 MB 이상의 객체를 빈번히 전송하는 경우라면 더욱 주의가 필요합니다.
전체적으로 Web Worker는 브라우저 환경에서 멀티 스레드 프로그래밍과 비슷한 이점을 제공하지만, 동시에 메시지 기반 통신 구조를 필요로 하며, DOM이 아닌 “순수 로직”만 처리할 수 있다는 제한이 있습니다. 따라서 큰 연산량이 필요한 부분을 워커로 분산시키는 용도로는 탁월하나, 무조건 모든 로직을 워커로 옮기는 식의 접근은 적절하지 않습니다.
Web Worker API를 사용하면 자바스크립트 코드에서 멀티 스레드 환경을 모방해, 메인 스레드가 렌더링과 사용자 입력을 처리하는 동안 무거운 작업을 백그라운드로 보낼 수 있습니다. 이는 사용자 경험(UX)을 개선할 수 있는 강력한 방법이지만, 스레드 간 통신 비용과 DOM 접근 제약 등을 잘 고려해야 합니다. 웹 애플리케이션에서 대규모 데이터 처리, 실시간 분석, 이미지·영상 처리, 암호화 작업 등에 Web Worker를 적용한다면, 반응성 높은 UI를 유지하면서도 복잡한 연산을 수행할 수 있습니다.