이벤트 루프는 여러분의 JavaScript를 단일 스레드에서 실행하므로, 오래 걸리는 동기적 작업은 무엇이든 그것을 블로킹합니다 — 그리고 블로킹되는 동안 Node는 다른 어떤 요청, 타이머, 콜백도 처리하지 못합니다. 블로킹은 거의 항상 CPU 바운드 작업이거나 *Sync API이며, 제대로 await된 비동기 I/O인 경우는 결코 없습니다.
*Sync) API — fs.readFileSync, fs.writeFileSync, child_process.execSync, zlib.gzipSync. 이들은 libuv에 넘기는 대신 그 자리에서 대기합니다.map/sort/filter, 행렬 연산, 이미지/비디오 처리, 거대한 구조의 파싱/직렬화.JSON.parse / JSON.stringify — 동기적이며 O(n); 수 MB 페이로드는 파싱 내내 루프를 얼립니다.crypto.pbkdf2Sync, bcrypt 동기 모드, 메인 스레드에서 큰 버퍼 해싱.while (true), 무한 재귀, 항목마다 작업이 있는 수백만 항목에 대한 for.// 이 모든 것이 루프를 블로킹합니다 — 그동안 다른 어떤 요청도 처리되지 않습니다:
const data = fs.readFileSync("big.json"); // 동기 I/O
const obj = JSON.parse(hugeString); // 큰 파싱
const hash = crypto.pbkdf2Sync(pw, salt, 1e6, 64, "sha512"); // 동기 암호화
for (let i = 0; i < 1e9; i++) { /* CPU */ } // 타이트한 루프
CPU 작업을
async함수로 감싸도 도움이 되지 않습니다 —async는await에서만 제어를 양보하며, 동기 본문은 여전히 루프 스레드에서 끝까지 실행됩니다.
perf_hooks.monitorEventLoopDelay() 또는 blocked-at 같은 라이브러리로 측정합니다; 지연이 증가한다는 것은 무언가가 스레드를 독점하고 있다는 뜻입니다.node --prof, clinic doctor/clinic flame, Chrome DevTools CPU 프로파일로 뜨거운 동기 프레임을 찾습니다.*Sync 호출의 비동기 대응물을 사용하세요 (fs.promises.readFile, crypto.pbkdf2, zlib.gzip).worker_threads 풀, 자식 프로세스, 또는 외부 큐/서비스로 옮기세요.setImmediate/await로 청크로 나누어 배치 사이에 루프가 숨 쉴 수 있게 하세요.const { Worker } = require("node:worker_threads");
// 비싼 해시를 오프로드하여 루프가 응답성을 유지하도록 합니다.
const worker = new Worker("./hash-worker.js", { workerData: { pw, salt } });
Node에서는 하나의 블로킹 호출이 그것을 유발한 요청뿐 아니라 모든 동시 클라이언트를 처벌합니다 — 지연 시간이 치솟고, 헬스 체크가 타임아웃되며, 프로세스가 부하 상태에서 "멈춘" 것처럼 보입니다. 면접관이 이것을 묻는 이유는 여러분이 구체적인 블로커(*Sync, 큰 JSON.parse, 동기 암호화, ReDoS, CPU 루프)를 짚어낼 수 있는지, 이벤트 루프 지연 도구로 감지할 수 있는지, 그리고 비동기로 전환하거나 워커 스레드로 오프로드하여 고칠 수 있는지 보기 위해서입니다. 이것은 프로덕션에서 가장 흔한 단일 Node 성능 장애입니다.
주니어부터 시니어까지 상세한 답변이 포함된 IT 면접 질문 라이브러리.
후원하기