Files
2nd/10_Wiki/Topic_Programming/Coding/Node_Worker_ChildProcess.md
T
Antigravity Agent 9148c358d0 docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를
Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류.

- 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로
  자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거,
  동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거.
- 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming,
  Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business,
  Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로,
  나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는
  title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백).
  원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지.
- 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서.
- 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는
  지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지.
- Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경.
- 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
2026-07-05 00:33:48 +09:00

5.1 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
node-worker-childprocess Node Worker / Child Process — CPU 작업 / 격리 Coding draft B conceptual 2026-05-09 2026-05-09
node
worker
child-process
cpu
vibe-coding
language applicable_to
TS / Node
Backend
worker_threads
child_process
fork
spawn
MessageChannel
transferList

Worker / Child Process

Node = single-threaded JS. CPU 무거운 작업 = worker_threads (같은 프로세스). 외부 명령 / 격리 = child_process. Cluster 는 다중 server (PM2 같은 거).

📖 핵심 개념

  • worker_threads: 같은 프로세스 다른 thread. 빠른 통신 (SharedArrayBuffer).
  • child_process: 별 프로세스. 격리 강함.
  • spawn / exec / fork: 다른 시작 방식.
  • Tinypool / piscina: worker pool.

💻 코드 패턴

worker_threads (CPU 작업)

// main.ts
import { Worker } from 'node:worker_threads';

const worker = new Worker(new URL('./hashWorker.ts', import.meta.url));
worker.on('message', (h) => console.log('hash:', h));
worker.postMessage({ data: 'hello' });
// hashWorker.ts
import { parentPort } from 'node:worker_threads';
import { createHash } from 'node:crypto';

parentPort!.on('message', ({ data }) => {
  const h = createHash('sha256').update(data).digest('hex');
  parentPort!.postMessage(h);
});

Worker pool (piscina)

import Piscina from 'piscina';

const pool = new Piscina({ filename: new URL('./worker.ts', import.meta.url).href });

// usage
const result = await pool.run({ data: 'x' });
const results = await Promise.all(items.map(i => pool.run(i)));

Transfer (zero-copy ArrayBuffer)

const buf = new ArrayBuffer(10_000_000);
worker.postMessage({ buf }, [buf]);
// buf 는 main 에서 더 사용 못 함 — transferred

child_process.spawn (외부 명령, streaming)

import { spawn } from 'node:child_process';

const ff = spawn('ffmpeg', ['-i', 'in.mp4', 'out.mp3']);
ff.stdout.on('data', (d) => log.info(d.toString()));
ff.stderr.on('data', (d) => log.warn(d.toString()));
ff.on('close', (code) => console.log('exit', code));

// async 변환
import { spawn } from 'node:child_process';
import { once } from 'node:events';

async function run(cmd: string, args: string[]): Promise<string> {
  const p = spawn(cmd, args);
  let out = ''; let err = '';
  p.stdout.on('data', (d) => { out += d; });
  p.stderr.on('data', (d) => { err += d; });
  const [code] = await once(p, 'close');
  if (code !== 0) throw new Error(`${cmd} failed: ${err}`);
  return out;
}

child_process.execFile (명령 + arg 안전)

import { execFile } from 'node:child_process';
import { promisify } from 'node:util';

const exec = promisify(execFile);
const { stdout } = await exec('git', ['log', '--oneline', '-10']);

⚠️ exec (shell) 보다 execFile 권장 — shell injection 방지.

child_process.fork (Node script)

import { fork } from 'node:child_process';

const child = fork('./jobRunner.ts');
child.send({ task: 'process' });
child.on('message', (r) => console.log(r));

Cluster (multi-server)

import cluster from 'node:cluster';
import { cpus } from 'node:os';

if (cluster.isPrimary) {
  for (let i = 0; i < cpus().length; i++) cluster.fork();
  cluster.on('exit', (w) => { cluster.fork(); }); // 자동 재시작
} else {
  app.listen(8080); // 각 worker 가 같은 포트 share
}

PM2 / systemd / pm2-runtime 가 권장.

MessageChannel (worker 끼리 통신)

const { port1, port2 } = new MessageChannel();
worker1.postMessage({ port: port1 }, [port1]);
worker2.postMessage({ port: port2 }, [port2]);
// 두 worker 가 직접 통신

Timeout / kill

const p = spawn('long-task');
const t = setTimeout(() => p.kill('SIGTERM'), 30_000);
p.on('close', () => clearTimeout(t));

CPU 측정 (어느 게 worker 필요?)

const start = process.hrtime.bigint();
heavyWork();
const ms = Number(process.hrtime.bigint() - start) / 1_000_000;
if (ms > 50) // 50ms 이상 = event loop block — worker 로

🤔 의사결정 기준

작업 추천
외부 CLI (ffmpeg, git) spawn / execFile
CPU heavy JS (압축, hash, ML) worker_threads / piscina
격리 / 별 메모리 child_process.fork
멀티 서버 process cluster + PM2
Quick command + small output execFile (promise)
Long stream spawn

안티패턴

  • exec(userInput): shell injection. execFile + arg 분리.
  • Worker 매번 새로: 시작 비용 큼. pool.
  • postMessage 큰 객체 (deep clone): 느림. transferList.
  • Timeout 없음 + child hang: 영원.
  • stderr 무시: 디버깅 어려움.
  • on('exit') 만 + 데이터 손실: 'close' 가 stdio 다 닫힌 후.
  • EventEmitter listener 누수: 매 spawn 마다 listener 추가.

🤖 LLM 활용 힌트

  • CPU = piscina worker pool.
  • 외부 명령 = execFile (안전).
  • 큰 데이터 = transferList ArrayBuffer.

🔗 관련 문서