[EXT-039][실무] 무거운 계산을 다른 실행 흐름으로 나누기 > IT 기술 공유

본문 바로가기
사이트 내 전체검색

IT 기술 공유

[EXT-039][실무] 무거운 계산을 다른 실행 흐름으로 나누기

페이지 정보

profile_image
작성자 기술팀장
댓글 0건 조회 5회 작성일 26-09-09 05:35

본문

[이번 수업]
Node.js의 주 실행 흐름은 자바스크립트 코드를 한 번에 하나씩 처리합니다. 큰 배열 계산처럼 CPU를 오래 쓰는 작업이 이 흐름을 붙잡으면 타이머와 요청 처리도 늦어집니다. 이번에는 작업 스레드(worker thread, 별도 실행 흐름)에 계산을 맡기고 결과만 돌려받습니다. 입출력 대기에는 기존 비동기 API가 보통 더 효율적이므로 CPU 계산에 초점을 맞춥니다.

[선수지식]
EXT-038에서 배운 구조화 복제와 전송의 차이, 배열과 이벤트의 기본 사용법이 필요합니다. worker_threads가 안정 API인 현재 지원 Node.js 환경을 준비하세요.

[학습목표]
1. 주 실행 흐름과 작업 스레드의 역할을 구분한다.
2. ArrayBuffer를 복사하지 않고 전송한다.
3. 계산 결과와 오류를 이벤트로 안전하게 받는다.

[핵심개념]
isMainThread는 현재 코드가 주 실행 흐름인지 알려 줍니다. new Worker()는 새 스레드에서 파일을 실행하며 workerData로 입력을 보냅니다. transferList에 ArrayBuffer를 넣으면 소유권이 작업 스레드로 이동합니다. 이때 보내는 쪽 버퍼는 분리(detached, 더는 내용을 쓸 수 없는 상태)되어 길이가 0이 됩니다. 작업 스레드는 parentPort.postMessage()로 결과를 보내고, 주 실행 흐름은 message 이벤트로 받습니다. error 이벤트도 반드시 처리해야 실패가 조용히 사라지지 않습니다. 실제 서버에서는 작업마다 새 스레드를 만들면 준비 비용이 커질 수 있으므로 개수를 제한한 작업자 풀을 사용합니다.

[따라하기]
아래 내용을 worker-sum.mjs로 저장하세요.

```js
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';

if (isMainThread) {
  const bytes = Uint8Array.from([10, 20, 30, 40]);
  const worker = new Worker(new URL(import.meta.url), {
    workerData: bytes.buffer,
    transferList: [bytes.buffer],
  });

  console.log(`mainBytes=${bytes.byteLength}`);
  const sum = await new Promise((resolve, reject) => {
    worker.once('message', resolve);
    worker.once('error', reject);
  });
  console.log(`sum=${sum}`);
} else {
  const bytes = new Uint8Array(workerData);
  const sum = bytes.reduce((total, value) => total + value, 0);
  parentPort.postMessage(sum);
}
```

Windows의 PowerShell·명령 프롬프트, macOS와 Linux의 터미널에서 같은 명령을 실행합니다.

```text
node worker-sum.mjs
```

예상 결과는 다음과 같습니다. 첫 줄은 버퍼가 복사되지 않고 이동했음을 보여 줍니다.

```text
mainBytes=0
sum=100
```

[흔한 실수]
아주 짧은 계산이나 파일·네트워크 대기까지 스레드로 옮기면 오히려 느려질 수 있습니다. 요청마다 Worker를 무제한 생성하지 마세요. 전송한 버퍼를 주 실행 흐름에서 다시 쓰거나, 서로 다른 스레드의 일반 변수가 자동으로 공유된다고 생각하는 것도 오류입니다. message만 듣고 error를 빼먹지 마세요.

[보안 주의]
작업 스레드에서도 신뢰할 수 없는 코드나 사용자가 지정한 임의 파일 경로를 실행하지 않습니다. 메시지의 자료형과 크기를 검사하고, 작업자 수·대기열 길이·실행 시간을 제한해 메모리와 CPU 고갈을 막으세요. 중단이 필요한 작업은 제한 시간을 정해 worker.terminate()로 정리하되, 본인 소유의 로컬·격리 환경에서 먼저 검증합니다. 비밀값과 원문 개인정보는 오류 로그에 남기지 않습니다.

[직접 해볼 과제]
숫자를 다섯 개 이상으로 늘려 합계를 다시 확인하세요. 이어서 transferList 줄만 빼고 실행하여 mainBytes가 원래 길이를 유지하는지 비교하고, 입력이 숫자가 아닐 때 작업 스레드가 오류 메시지를 보내도록 검증 로직을 추가해 보세요.

[확인문제]
1. CPU 계산을 주 실행 흐름에서 오래 수행하면 다른 이벤트가 왜 늦어질까요?
2. transferList로 보낸 ArrayBuffer의 보내는 쪽 byteLength는 왜 0이 될까요?
3. 실제 서버에서 요청마다 새 Worker를 만드는 대신 제한된 작업자 풀이 필요한 이유는 무엇일까요?

[다음 학습]
다음에는 제한된 수의 작업 스레드와 대기열을 묶어 작업자 풀을 만들고, 작업이 몰릴 때 새 요청을 늦추는 역압(backpressure)을 적용합니다.

[공식 참고 자료]
Node.js Worker threads
https://nodejs.org/api/worker_threads.html
Node.js Worker 생성자
https://nodejs.org/api/worker_threads.html#new-workerfilename-options
Node.js workerData
https://nodejs.org/api/worker_threads.html#workerdata
WHATWG 구조화 전송 알고리즘
https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializewithtransfer

댓글목록

등록된 댓글이 없습니다.

회원로그인

회원가입

사이트 정보

회사명 : 회사명 / 대표 : 대표자명
주소 : OO도 OO시 OO구 OO동 123-45
사업자 등록번호 : 123-45-67890
전화 : 02-123-4567 팩스 : 02-123-4568
통신판매업신고번호 : 제 OO구 - 123호
개인정보관리책임자 : 정보책임자명

접속자집계

오늘
3,939
어제
6,862
최대
16,772
전체
769,890
Copyright © 소유하신 도메인. All rights reserved.