본문 바로가기
개발자 전향 프로젝트

웹 워커(Web Worker)란? 언제 쓰는 걸까? feat.타이머

by 샘오리 2024. 2. 16.
728x90
반응형
브라우저가 인식할 수 있는 몇 안되는 언어인 자바스크립트는 놀랍게도 싱글 스레드이다.


싱글 스레드가 뭔지 모른다면 이전글을 참조하길 바란다.

 

https://samori.tistory.com/86

 

싱글스레드 vs 멀티스레드

이 질문에 대답을 하기 전에 스레드가 뭔지를 알아야한다. 우리가 자주 쓰는 프로그램, 뭐 크롬이라는 브라우저를 예로 들어보자. 크롬은 사용자가 클릭해서 실행할 때 컴퓨터로부터 메모리라

samori.tistory.com

싱글 스레드 이기 때문에 몇가지 한계가 존재한다.


싱글 스레드의 한계

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

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

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

 

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

쓰로틀링이란?

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

 

이를 해결 할 수 있는 게 바로 웹 워커이다.

 

웹워커란?

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

그렇다면 웹 워커를 당장 도입하면 되는거 아닌가?


웹 워커를 도입할 때 주의해야 하는 이유

 

웹워커는 장점과 단점이 뚜렷하다. 그래서인지 해외 개발 커뮤니티를 보더라도

웹워커를 정말 필요로 할 때만 쓰는 것을 추천하는 글이 많다.

이유는 이전글에 설명했다싶이 모든 것은 비용이기 때문이다. 메모리는 유한하다.

간단한 로직이라면 싱글 스레드로도 충분한데 굳이 브라우저의 백그라운드 스레드를 사용하는게 배보다 배꼽이 더 클 수 있다는 것이다.  이전 글에서 설명했듯 멀티 스레드를 사용하게 되면 컨텍스트 스위칭이 잦게 발생하면서 오버헤드 비용이 발생할 수 있고 그렇게 되면 성능 문제가 생기기 때문에 주의를 기울여야 한다는 것이다.

 

하지만 필요한곳에 적절하게 사용한다면 더할나위 없이 좋은 기능이라서 이번 글에서 다뤄보려고 한다.

 

웹 워커를 사용하여 타이머 기능을 구현하는 방법은 다음과 같다.


웹 워커 사용하기

참고로 웹  워커는 DOM에 직접 접근하지 못하기 때문에 메인 스레드와 서로 메시지를 주고 받아서 통신한다.

postMessage로 메세지를 보내고, onMessage로 메세지를 받는다.

가장 먼저 이걸 이해해야 웹 워커를 이해할 수 있다.

 

일단 파일을 분리한다.

 

1) 메인 자바스크립트 파일과

2) 웹 워커 로직이 있는 파일

 

쉬운 이해를 위해 샘플은 최대한 간단하게 만들었다.

 

아래와 같이 1초마다 postMessage라는 함수에 날짜 정보를 넣는 함수가 있다고 가정해보자.

 

웹 워커 로직이 들어가있는 worker.js이다.

onmessage = function (e) {
    if (e.data === "start") {
      this.intervalId = this.setInterval(() => {
        postMessage(new Date)
      }, 1000)
    }
    if (e.data === "stop") {
      clearInterval(this.intervalId)
    }
  }

 

위 로직이 들어간 worker.js 파일을 메인 자바스크립트 파일에서 호출한다. 

테스트 편의를 위해 script를 html에 inline으로 넣었다.

<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div id="timer"></div>

<script type="text/javascript">
    const myWorker = new Worker('./worker.js');
    myWorker.postMessage('start')

    const timerEl = document.querySelector('#timer')
    myWorker.onmessage = function (e) {
        timerEl.innerHTML = e.data
    }
    
</script>
</html>

 

위 소스를 간략하게 설명하자면,

timer라는 id를 가진 div를 선언하고 worker라는 js파일을 호출해서 myWorker라는 상수에 담아준다.

그리고 myWorker에 'start'라는 메세지를 보낸다. 그러면 worker.js가 post 된 메세지를 onmessage로 받는데

message의 내용이 'start'였으니 분기문을 타고 아래 1초마다 돌아가는 setInterval 함수가 실행되는 것이다.

 

아무튼 그렇게 1초마다 도는 스코프에서 우리는 return의 의미로 post 를 다시 할 것인데 어떤 것을 return 할 것이냐면 바로

날짜이다. 이렇게 되면 날짜 정보가 1초마다 업데이트되면서 return 된다.

onmessage = function (e) {
    if (e.data === "start") {
      this.intervalId = this.setInterval(() => {
        postMessage(new Date)
      }, 1000)
    }
    if (e.data === "stop") {
      clearInterval(this.intervalId)
    }
  }

 

return된 데이터는 onmessage로 받아줘야 하며 받은 데이터를 위에 html body에 선언한 div 태그 값으로 넣어준다.

    myWorker.onmessage = function (e) {
        timerEl.innerHTML = e.data
    }

 

그렇게 되면 날짜 정보가 계속해서 바뀌는 것을 볼 수 있다.

 

이제 이 타이머는 특수한 경우가 아닌 이상 브라우저의 탭이 비활성화되어도 계속 같은 시간마다 반복적으로 돌 것이다.

 

같은 원리로 제어할 수 있다. start 대신 stop을 보내면 되지 않을까?

    myWorker.postMessage('start') X
    myWorker.postMessage('stop') O

 

그렇게 되면 worker.js에서  if (e.data === "stop")  라는 분기문을 타고 해당 setInterval 함수를 끝낼 수 있다.

onmessage = function (e) {
    if (e.data === "start") {
      this.intervalId = this.setInterval(() => {
        postMessage(new Date)
      }, 1000)
    }
    if (e.data === "stop") {
      clearInterval(this.intervalId)
    }
  }
728x90
반응형