Node.js 기본 구조

graph TB
    subgraph STACK[Call Stack]
        S1[현재 실행 중인 함수]
    end

    subgraph LOOP[Event Loop]
        EL[Call Stack 비었나?<br/>→ Queue에서 가져오기]
    end

    subgraph QUEUES[Queues]
        MQ[Microtask Queue<br/>Promise, await 완료]
        TQ[Task Queue<br/>setTimeout, I/O, Cron]
    end

    STACK --> |비어있을 때만| LOOP
    LOOP --> MQ
    MQ --> |비었으면| TQ
    TQ --> STACK

    style STACK fill:#ff6b6b
    style MQ fill:#4ecdc4
    style TQ fill:#45b7d1

핵심 규칙: Call Stack이 비어야만 다음 작업 실행


오해 1: “setTimeout(0)은 즉시 실행된다”

코드

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

실행 흐름

sequenceDiagram
    participant CS as Call Stack
    participant TQ as Task Queue
    participant OUT as 출력

    Note over CS: console.log('1')
    CS->>OUT: "1"

    Note over CS: setTimeout 등록
    CS->>TQ: 콜백 등록 (0ms여도!)

    Note over CS: console.log('3')
    CS->>OUT: "3"

    Note over CS: Call Stack 비었음!
    TQ->>CS: 콜백 가져오기

    Note over CS: 콜백 실행
    CS->>OUT: "2"

출력: 1 → 3 → 2

setTimeout(0)은 “다음 Event Loop에 실행"이라는 뜻!


오해 2: “await은 프로그램을 멈춘다”

코드

async function fetchData() {
  console.log("fetch 시작");
  await sleep(3000);
  console.log("fetch 끝");
}

fetchData();
console.log("다음 코드");

실행 흐름

sequenceDiagram
    participant CS as Call Stack
    participant MQ as Microtask Queue
    participant OUT as 출력

    Note over CS: fetchData() 실행
    CS->>OUT: "fetch 시작"

    Note over CS: await 만남!
    CS-->>MQ: Promise 등록, 제어권 반환
    Note over CS: fetchData 일시 중단

    Note over CS: 다음 코드 실행 가능!
    CS->>OUT: "다음 코드"

    Note over CS: Call Stack 비었음

    Note over MQ: 3초 후 Promise 완료
    MQ->>CS: fetchData 재개
    CS->>OUT: "fetch 끝"

출력: fetch 시작 → 다음 코드 → (3초 후) fetch 끝


오해 3: “Promise와 setTimeout은 같은 우선순위”

코드

console.log("1");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("promise"));
console.log("2");

실행 흐름

sequenceDiagram
    participant CS as Call Stack
    participant MQ as Microtask Queue
    participant TQ as Task Queue
    participant OUT as 출력

    Note over CS: 동기 코드 실행
    CS->>OUT: "1"
    CS->>TQ: setTimeout 콜백 등록
    CS->>MQ: Promise.then 등록
    CS->>OUT: "2"

    Note over CS: Call Stack 비었음!

    Note over MQ: Microtask 먼저!
    MQ->>CS: Promise 콜백
    CS->>OUT: "promise"

    Note over TQ: 그 다음 Task
    TQ->>CS: setTimeout 콜백
    CS->>OUT: "timeout"

출력: 1 → 2 → promise → timeout


오해 4: “await은 동기처럼 동작한다”

비교

graph LR
    subgraph SYNC[진짜 동기 대기]
        A1[while로 CPU 점유] --> A2[전체 프로그램 블록]
        A2 --> A3[아무것도 못함]
    end

    subgraph AWAIT[await 대기]
        B1[Promise 등록] --> B2[제어권 반환]
        B2 --> B3[다른 코드 실행 가능!]
    end

    style A3 fill:#ff6b6b
    style B3 fill:#4ecdc4
// ❌ 진짜 동기 (전체 블록)
function syncWait(ms) {
  const end = Date.now() + ms;
  while (Date.now() < end) {} // CPU 점유!
}

// ✅ await (해당 함수만 중단)
await new Promise((r) => setTimeout(r, 3000));

오해 5: “for 안의 await은 병렬”

순차 실행 (기본)

gantt
    title for-await 순차 실행
    dateFormat X
    axisFormat %s초

    section 처리
    item 1 :0, 1
    item 2 :1, 2
    item 3 :2, 3
for (const item of items) {
  await processItem(item); // 하나씩 순차!
}
// 총 3초

병렬 실행 (Promise.all)

gantt
    title Promise.all 병렬 실행
    dateFormat X
    axisFormat %s초

    section 처리
    item 1 :0, 1
    item 2 :0, 1
    item 3 :0, 1
await Promise.all(items.map((item) => processItem(item)));
// 총 1초!

Event Loop 흐름도 (핵심!)

flowchart TD
    START([시작]) --> CS{Call Stack에<br/>동기 코드 있음?}

    CS -->|예| EXEC[실행!<br/>끝날 때까지 독점]
    EXEC --> POP[Call Stack에서 제거]
    POP --> CS

    CS -->|아니오| MQ{Microtask Queue에<br/>작업 있음?}

    MQ -->|예| MICRO[모든 Microtask 실행<br/>Promise.then, await 완료]
    MICRO --> MQ

    MQ -->|아니오| TQ{Task Queue에<br/>작업 있음?}

    TQ -->|예| TASK[하나만 Call Stack에<br/>올려서 실행]
    TASK --> CS

    TQ -->|아니오| IDLE[대기 idle]
    IDLE --> CS

    style EXEC fill:#ff6b6b
    style MICRO fill:#4ecdc4
    style TASK fill:#45b7d1

우선순위

graph LR
    A[1️⃣ Call Stack<br/>동기 코드] --> B[2️⃣ Microtask Queue<br/>Promise, await]
    B --> C[3️⃣ Task Queue<br/>setTimeout, I/O]

    style A fill:#ff6b6b,color:#fff
    style B fill:#4ecdc4,color:#fff
    style C fill:#45b7d1,color:#fff

핵심 요약

오해실제
setTimeout(0)은 즉시 실행❌ Task Queue 경유 후 실행
await은 프로그램을 멈춤❌ 해당 함수만 중단, 다른 코드 실행 가능
Promise와 setTimeout 우선순위 같음❌ Microtask(Promise)가 먼저
await은 동기 코드❌ 비동기, Event Loop에 제어권 반환
for-await은 병렬❌ 순차 실행, 병렬은 Promise.all

한 줄 정리

await = “나(이 함수)는 여기서 기다릴게, Event Loop는 그 동안 다른 일 해!”