🔄 이벤트 루프
📖 정의
**이벤트 루프(Event Loop)**는 JavaScript가 단일 스레드임에도 불구하고 비동기 작업을 처리할 수 있게 해주는 메커니즘입니다. Call Stack, Task Queue, Microtask Queue를 조율하여 코드 실행 순서를 관리합니다.
🎯 비유로 이해하기
식당 주방 비유
이벤트 루프를 식당 주방에 비유하면:
주방장 (Call Stack)
├─ 한 번에 하나의 요리만 가능
├─ 현재 만들고 있는 요리에 집중
└─ 요리 완성되면 다음 요리 시작
주문 게시판 (Task Queue)
├─ 일반 주문들이 대기
├─ setTimeout, 이벤트 등
└─ 주방장이 한가할 때 처리
VIP 주문 (Microtask Queue)
├─ Promise, async/await
├─ 일반 주문보다 우선순위 높음
└─ 주방장이 한가하면 VIP부터
웨이터 (Event Loop)
├─ 주방장 상태 확인
├─ 한가하면 VIP 주문 전달
├─ VIP 없으면 일반 주문 전달
└─ 계속 순환하며 확인
과정:
1. 주방장이 요리 중 (Call Stack 실행)
2. 요리 완료 (Stack 비움)
3. 웨이터가 VIP 주문 확인 (Microtask)
4. VIP 주문 있으면 주방장에게 전달
5. VIP 주문 없으면 일반 주문 확인 (Task)
6. 일반 주문 주방장에게 전달
7. 계속 반복
은행 창구 비유
창구 직원 (JavaScript 엔진)
└─ 한 명만 있음 (단일 스레드)
처리 중인 업무 (Call Stack)
└─ 현재 처리하는 고객
일반 대기번호 (Task Queue)
├─ 입출금
├─ 계좌 조회
└─ setTimeout, setInterval
우선 대기번호 (Microtask Queue)
├─ 긴급 업무
├─ Promise
└─ async/await
매니저 (Event Loop)
├─ 직원이 한가한지 계속 확인
├─ 우선 번호부터 처리
└─ 일반 번호 처리
실제 상황:
직원: "다음 손님~"
매니저: "우선 번호 있나요?" (Microtask 확인)
매니저: "없으면 일반 번호" (Task 확인)
직원: 업무 처리
매니저: 다시 확인 (Loop)
놀이공원 놀이기구 비유
놀이기구 (JavaScript 실행)
└─ 한 번에 한 팀만 탑승
현재 탑승 중 (Call Stack)
└─ 지금 타고 있는 사람들
일반 줄 (Task Queue)
├─ 일반 티켓 소지자
├─ setTimeout
└─ 이벤트 핸들러
패스트패스 줄 (Microtask Queue)
├─ 우선권 소지자
├─ Promise
└─ async/await
직원 (Event Loop)
1. 놀이기구 비었나요? (Stack 확인)
2. 패스트패스 있나요? (Microtask)
3. 패스트패스 전부 태우기
4. 일반 줄에서 1팀 태우기
5. 다시 1번으로
특징:
- 놀이기구는 항상 한 팀만
- 패스트패스가 항상 우선
- 일반 줄은 한 번에 1팀씩만
⚙️ 작동 원리
1. JavaScript 실행 모델
// JavaScript는 단일 스레드
/*
JavaScript 실행 환경:
┌────────────────────────────────────┐
│ JavaScript Engine │
│ │
│ ┌──────────────────────────┐ │
│ │ Call Stack │ │ 동기 코드 실행
│ │ (실행 중인 함수들) │ │
│ └──────────────────────────┘ │
│ │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Web APIs │
│ (브라우저 / Node.js 제공) │
│ │
│ • setTimeout │
│ • setInterval │
│ • fetch (HTTP 요청) │
│ • DOM Events │
│ • Promise │
│ │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Microtask Queue │
│ (높은 우선순위) │
│ │
│ • Promise.then() │
│ • async/await │
│ • queueMicrotask() │
│ • MutationObserver │
│ │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│ Task Queue │
│ (Macrotask Queue) │
│ (낮은 우선순위) │
│ │
│ • setTimeout │
│ • setInterval │
│ • setImmediate (Node.js) │
│ • I/O 작업 │
│ • UI 렌더링 │
│ │
└────────────────────────────────────┘
↓
┌─────────────┐
│ Event Loop │ ← 계속 순환
└─────────────┘
*/
// 실행 순서:
// 1. Call Stack의 동기 코드 실행
// 2. Stack이 비면 Microtask Queue 확인
// 3. Microtask를 모두 실행
// 4. Task Queue에서 하나 실행
// 5. 1번으로 돌아가기
2. Call Stack (호출 스택)
// Call Stack: 실행 중인 함수들의 스택
function first() {
console.log('첫 번째');
second();
console.log('첫 번째 끝');
}
function second() {
console.log('두 번째');
third();
console.log('두 번째 끝');
}
function third() {
console.log('세 번째');
}
first();
// 실행 과정:
/*
Step 1: first() 호출
┌─────────────┐
│ first() │
└─────────────┘
출력: "첫 번째"
Step 2: second() 호출
┌─────────────┐
│ second() │
├─────────────┤
│ first() │
└─────────────┘
출력: "두 번째"
Step 3: third() 호출
┌─────────────┐
│ third() │