C
JavaScript/비동기/Lesson 15

이벤트 루프 — *JS 가 단일 스레드인데 빠른 이유*

45분·theory
이 챕터
1/3

이벤트 루프 — *JS 가 단일 스레드인데 빠른 이유*

🎯 이 lesson 을 읽고 나면

이 lesson 을 다 읽고 나면 아래 3가지를 자신 있게 할 수 있습니다.

  • ✅ 이벤트 캡처링 · 버블링 · 위임 (delegation) 패턴
  • ✅ addEventListener cleanup 누락의 메모리 누수
  • ✅ debounce / throttle 의 적용 시점

학습 목표를 체크리스트로 두고 다 답할 수 있게 되면 lesson 을 닫으세요.

단일 스레드 + 이벤트 루프

핵심 한 줄

JS 는 한 번에 하나씩만 처리하는 단일 스레드 언어인데도 동시에 수많은 작업 을 처리할 수 있습니다. 비밀은 이벤트 루프 (Event Loop) + 비동기 작업의 외부 위임.

단일 스레드 — 함정?

javascript
console.log("1");
slowOperation();        // 5초 걸림
console.log("2");

slowOperation 이 5초 걸리면 그 동안 다른 일 못 함. 사용자 클릭도 무시. 이게 블로킹.

해결책: 블로킹 작업을 외부에 맡기고 JS 는 계속 다음 일을 합니다.

이벤트 루프 동작

code
┌──────────────────┐
│  Call Stack      │   ← 현재 실행 중 함수들
│  (단일 실행)      │
└────────┬─────────┘
         │ 비동기 작업 끝나면
         │
┌────────▼─────────┐
│   Task Queue     │   ← setTimeout·이벤트 등 대기열
└────────┬─────────┘
         │ Stack 비면 가져옴
         ▼
       Event Loop (계속 순회)

브라우저·Node.js 가 별도 스레드 에서 비동기 작업 (타이머·네트워크·파일) 처리. 끝나면 Queue 에 콜백을 넣고, Stack 이 비면 Event Loop 가 가져와 실행.

가장 유명한 퀴즈

javascript
console.log("1");
setTimeout(() => console.log("2"), 0);
console.log("3");

// 출력: 1, 3, 2

setTimeout(..., 0) 인데 왜 2마지막?

  • setTimeout 콜백은 Task Queue
  • 현재 코드 (console.log("3")) 끝난 에 처리

setTimeout지연 시간 0즉시 가 아니라 현재 코드 끝나면 가능한 빨리.

Microtask vs Macrotask

Queue 가 2종류:

  • Microtask — Promise·queueMicrotask. 우선
  • Macrotask — setTimeout·이벤트·I/O. 나중
javascript
console.log("1");
setTimeout(() => console.log("2"), 0);          // Macrotask
Promise.resolve().then(() => console.log("3"));  // Microtask
console.log("4");

// 출력: 1, 4, 3, 2

현재 코드 끝나면:
1. Microtask 모두 처리 (3)
2. 그 다음 Macrotask 하나 (2)

비동기 = 외부에 맡기기

javascript
// 동기 — JS 가 직접 처리
const x = 5 + 3;

// 비동기 — 외부에 맡김
setTimeout(() => {}, 1000);       // 브라우저 타이머
fetch('/api');                     // 브라우저 네트워크
fs.readFile(...);                  // Node 파일 시스템

이들은 JS 가 아닌 환경 (브라우저·Node) 이 처리하고 완료 시 콜백 넣어줍니다. JS 본체는 기다리지 않고 다음 코드 계속.

왜 빠른가

10000명이 동시 접속해도 각자 다른 스레드 안 만들고:

  • 요청 1 시작 → DB 조회 외부 위임 → 다른 요청 처리
  • 요청 2 시작 → DB 조회 위임 → 또 다른 요청
  • DB 응답 오면 → 각각 콜백 처리

스레드 1개로 수많은 동시 작업. Node.js·Nginx 가 적은 메모리로 많은 트래픽 처리하는 비결.

함정 — CPU 바운드 작업

javascript
// ❌ 큰 계산 — 메인 스레드 막힘
for (let i = 0; i < 1e9; i++) {
    sum += i;
}
// 그 동안 클릭·렌더링 모두 멈춤

JS 가 비동기 강점I/O 작업 에만 적용. CPU 작업 은 여전히 블로킹.

해결:

  • 작은 청크로 나눠 setTimeout 으로
  • Web Worker (브라우저) / Worker Threads (Node) — 별도 스레드

한 번 정리

  • JS = 단일 스레드 + 이벤트 루프
  • 비동기 작업은 외부에 위임
  • Microtask (Promise) > Macrotask (setTimeout)
  • I/O 강점, CPU 약점
  • CPU 작업은 Worker

⚡ 직접 해보기 — Microtask vs Macrotask 순서

같은 코드의 출력 순서를 예측해보고 실행. Promise(microtask) > setTimeout(macrotask).
✏️ JS 코드
📟 콘솔 출력
▶ 실행 버튼을 눌러보세요
⚠️ 브라우저 샌드박스에서 실행 — console.log()만 지원, alert/fetch 불가

🤖 AI 에게 이렇게 요청해보세요

이 lesson 의 개념을 알면 AI 에게 구체적으로 지시할 수 있습니다. 막연한 "고쳐줘" 가 아니라 어휘를 가진 요청 — 그게 토큰 절약의 출발점입니다.

  • "이 클릭 이벤트에 debounce 300ms 적용해줘"
  • "이 이벤트 핸들러를 cleanup (return () => removeEventListener) 까지 추가해줘"

왜 이게 토큰을 줄이나

개념을 모를 땐 AI 답변을 받고도 "그게 뭐예요?" 를 다시 물어야 합니다. 그 "다시 물음" 이 토큰을 잡아먹습니다. 개념 한 번 익혀두면 대화가 한 번에 끝납니다.

먼저 읽으면 좋은 개념: 이벤트 처리
다음 추천: Promise 프로미스
이벤트 루프 - JavaScript