Next.js에서 Web Worker 사용해보기

JS (TIL & Remind)·2022년 5월 6일
3
post-custom-banner

Web Worker란?

Web Worker는 웹 컨텐츠를 위해서 Background Thread 에서 스크립트를 실행할 간편한 방법을 제공합니다. Worker Thread는 사용자 인터페이스(UI)를 방해하지 않고 작업을 수행할 수 있습니다.
출처 - https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API/Using_web_workers

자바스크립트는 Single Thread로 작동하지만, 브라우저는 그렇지 않다.

따라서 브라우저에서 처리되는 네트워크 통신이나 I/O는 서로 다른 Thread에서 작동한다.

여기서 Web Worker를 통해 브라우저의 렌더링을 담당하는 Main Thread를 방해하지 않고 별도의 Thread를 생성하여 자바스크립트를 실행할 수 있다.

Web Worker 주의점

Web Worker는 위에서도 말했듯이 Main Thread와는 별개의 Thread를 가진다.

따라서, Worker Thread는 Main Thread가 가지는 전역 스코프와는 다른 스코프를 가지게 된다.

어떻게 다른지 로그를 남겨 확인해보자.

💡 **self는 전역 스코프 객체에 접근하는 키워드이다.** [https://geundung.dev/101](https://geundung.dev/101)
// worker를 호출하는 컴포넌트
// components/Callee.tsx
import SimpleWorker from 'workers/simple.worker';

const Callee = () => {
	
	useEffect(() => {
		const simpleWorker = new SimpleWorker();
		
		simpleWorker.postMessage('call worker');

		console.log('Main Thread의 self', self);
	}, []);
}

// worker
// simple.worker.ts
self.addEventListener('message', (e) => {
		console.log('Worker Thread의 self', self);
});

Main Thread의 전역 스코프 객체는 Window 객체이다.

Worker Thread의 전역 스코프 객체는 DedicatedWorkerGlobalScope 객체이다.

이제 주의할 점이 나오는데, Web Worker(Worker Thread)의 스코프에서는 할 수 없는 것들이 몇가지 있다.

  1. DOM을 조작할 수 없다.
  2. 일부 Window의 메서드를 이용할 수 없다. (WebSocket, IndexedDB 등 제외)
  3. Window 객체에 접근할 수 없다.

따라서, 이 점을 주의해서 코드를 작성해야 한다.

Next.js 에서 Web Worker를 사용하기 위한 세팅

Next.js에서 Web Worker를 사용하기 위해 몇가지 세팅을 해줘야 하는데, Next.js의 버전마다 조금씩 다르다.

해당 글은 Next 9.5.5 (Webpack 4) 를 기준으로 작성되었다.

Next 10 이상 (Webpack 5) 은 다음 글을 참고하면 된다.

Next.js With Web Worker Example - StackBlitz

Next 9.x 버전 (Webpack 4)

  1. worker-loader 패키지를 설치한다.
npm i worker-loader
  1. next.config.json에서 다음과 같이 webpack 설정을 해준다.
// next-compose-plugins 사용을 기준으로 작성
const withPlugins = require('next-compose-plugins');

module.exports = withPlugins([], {
	webpack(config, options) {
		config.module.rules.push({
				test: /\.worker\.ts$/, // Typescript
				use: {
					loader: 'worker-loader',
				},
		});
	}
});

사용법

Worker Thread는 Main Thread와 서로 postMessage를 통해 메시지를 전달하고,

onmessage 리스너를 통해 메시지를 받는다.

Worker

// workers/simple.worker.ts
self.onmessage = (e) => {
		const message = e.data;
		
		// Main Thread 한테 받은 메시지
		console.log(message);
		
		self.postMessage('Worker Thread에서 보낸 메시지');
};

// Typescript 컴파일 에러를 방지하기 위한 코드
export default class SimpleWorker extends Worker {
	constructor() {}
}

Components

// components/Callee.tsx
import SimpleWorker from 'workers/simple.worker';
import { useEffect } from 'react';

const Callee = () => {
		useEffect(() => {
				const simpleWorker = new SimpleWorker();

				simpleWorker.postMessage('Main Thread에서 보낸 메시지');

				simpleWorker.onmessage = (e) => {
						// Worker Thread 한테 받은 메시지
						console.log(e.data);
						simpleWorker.terminate(); // 워커를 사용할 필요가 없다면 종료
				}
		}, []);
}

Callee 컴포넌트가 마운트 되면 다음과 같은 결과가 나온다.

마치며

본인의 경우, 서버에서 받아온 데이터를 복호화 할 때 Web Worker를 사용했다.

많은 양의 암호화 된 String 데이터를 복호화 하는 스크립트 처리 속도가 오래 걸렸기 때문에 이 과정에서 로딩 스피너 애니메이션이 멈추고, 사용자 인터랙션을 막으며 기기 사양에 따라 브라우저가 렌더링 자체를 블락시켜 버리는 경우도 나왔었다.

Web Worker를 사용한다고 해서 복호화 계산이 빨라지는 것은 아니지만, 적어도 브라우저 렌더링을 막지 않기 때문에 UX 개선에 효과적이었다.

이 외에도 브라우저의 렌더링을 막지 않으면서 백그라운드에서 무언가 처리해야 할 때도 유용할 것 같다. 기본적인 개념만 알아두고 상황에 따라 더 깊게 공부 할 예정이다.

profile
노션에 더욱 깔끔하게 정리되어있습니다. (하단 좌측의 홈 모양 아이콘)
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 1월 27일

예시가 잘 작동하지 않는것 같아요

답글 달기