TypeScript/비동기/Lesson 02
이벤트 루프 — Microtask vs Macrotask, 타입으로 큐를 표현하기
30분·theory
이 챕터
2/7
TypeScript
이벤트 루프 — Microtask vs Macrotask, 타입으로 큐를 표현하기
💡 왜 배워야 할까요? — 큐가 보이면 디버깅이 보인다
🎯
Promise 가 setTimeout 보다 먼저 실행되는 이유 = microtask queue 가 macrotask queue 보다 우선. 면접 단골 질문.
💼
TS 는 큐를 바꾸지 않지만, 큐에 들어가는 함수의 시그니처를 강제할 수 있습니다. 잘못된 콜백을 큐에 넣는 실수를 컴파일 타임에 차단합니다.
⚡
`queueMicrotask`·`setImmediate`·`requestAnimationFrame` 의 콜백 시그니처를 TS 가 정확히 알고 있어, 잘못된 호출 패턴을 막아줍니다.
🔗
비동기 코드의 순서 버그(레이스 컨디션)는 타입 시스템이 잡아주지 못합니다 — 하지만 큐 모델을 이해해야 그런 버그를 디버깅할 수 있습니다.
🏢 실무에서는
React 의 `useEffect`, Next.js 의 `useTransition`, Vue 의 `nextTick` — 모두 microtask·macrotask 큐 위에 올라가 있습니다. `setState` 직후 DOM 을 읽으면 옛 값이 나오는 이유, `await` 한 줄 사이에 props 가 바뀔 수 있는 이유 — 전부 이벤트 루프 모델로 설명됩니다.
Call Stack → Web API → Queue → Loop
1. 이벤트 루프의 4 구성 요소
2. Loop 의 한 사이클
1. Call Stack 이 비면 Microtask Queue 를 전부 비웁니다.
2. 그 다음 Macrotask Queue 에서 1개만 꺼냅니다.
3. 다시 Microtask Queue 를 비웁니다.
4. 반복.
3. TS 가 보호해주는 부분
4. async/await 가 큐와 만나는 지점
💡 💡 이벤트 루프 면접 단골 + TS 가 잡아주는 실수
1. Promise 가 setTimeout(0) 보다 먼저 실행되는 이유
microtask 큐 우선. 한 사이클 안에서 microtask 가 전부 비워진 다음에야 macrotask 1개를 처리.
2. await 이후의 코드는 항상 microtask 로
3. queueMicrotask 콜백은 인자가 없다 — TS 가 강제
4. setTimeout 의 가변 인자는 TS 가 알고 있다
5. 무한 microtask 는 메인 스레드를 멈춘다
TS 가 막아주지 못함 — 코드 리뷰로 차단.
⚡ 직접 실행해보기 — 이벤트 루프 순서
동기 → microtask → macrotask 순서를 직접 확인합니다.
✏️ JS 코드
📟 콘솔 출력
▶ 실행 버튼을 눌러보세요
⚠️ 브라우저 샌드박스에서 실행 — console.log()만 지원, alert/fetch 불가
확인 퀴즈
다음 코드의 출력 순서는?
```ts
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
```
먼저 읽으면 좋은 개념: 동기 vs 비동기 — 콜백 시그니처까지 보호
다음 추천: Promise<T> — JS vs TS 차이