이벤트 루프란? JavaScript가 여러 작업을 처리하는 순서를 쉽게 이해하기
이벤트 루프는 JavaScript가 대기 중인 작업을 어떤 순서로 메인 스레드에 올려 처리할지 관리하는 방식입니다. Call Stack·Task·Microtask·Promise·async/await·Long Task·INP까지 초보 기준으로 정리합니다.
한 번에 하나인데 여러 일이 보이는 이유
「JavaScript는 한 번에 하나만」이라고 하면서도 클릭·타이머·서버 요청·Promise가 겹쳐 보입니다. 이벤트 루프(Event Loop)는 실행할 작업들을 어떤 순서로 메인 스레드에 올려 처리할지 관리하는 방식입니다. 은행 창구 직원 한 명 + 대기표와 같습니다 — 지금 고객이 끝나면 다음 번호를 부릅니다. 현재 실행 중인 함수가 쌓이는 곳이 Call Stack(LIFO)이고, Stack이 비었는지 보며 대기열에서 다음을 가져옵니다.
Long Task · Task · Microtask
Long Task가 돌면 그 사이 클릭·타이머는 Queue에서 기다립니다. 끝나면 이벤트 루프가 다음 기회를 만듭니다 — TBT·INP와 직결됩니다. setTimeout은 JS가 1초 멈추는 것이 아니라 Web APIs(브라우저)가 시간을 재고 콜백을 Task Queue에 넣습니다. setTimeout(..., 0)도 현재 코드·Microtask 뒤입니다. 시간이 「지나면 실행 가능」일 뿐 Stack이 바쁘면 더 늦습니다. Promise 후속은 Microtask Queue에서 다음 Task보다 먼저 처리되는 경우가 많습니다.
console.log A → setTimeout 0 → Promise.then → console.log D → 보통 A D C(Microtask) B(Task) 단순 흐름: Task 종료 → Microtask → 렌더 기회 → 다음 Task → 반복
비동기 · async/await · 렌더링
비동기 ≠ 병렬. 네트워크·타이머는 브라우저가 맡아 JS가 기다리는 동안 다른 일을 할 수 있게 보입니다. async/await의 await는 해당 async 함수만 이어가기를 멈추고 브라우저 전체가 멈추지는 않습니다. 응답 뒤 무거운 동기 계산이면 여전히 Long Task입니다. await heavy()가 계산을 Worker로 옮기지 않습니다. fetch는 빨라도 Stack이 바쁘면 화면 반영이 늦을 수 있어 서버 지연과 「브라우저가 바쁜가」를 구분합니다. Task가 길면 Paint·애니메이션(Jank)·스피너 정지 힌트가 됩니다. 버튼에 「처리 중」을 써도 긴 동기 작업이 이어지면 Paint가 밀릴 수 있어 Yield·작업 나누기가 필요합니다. Microtask를 끝없이 만들면 Starvation으로 다음 Task·렌더가 밀릴 수 있습니다. Debounce·Throttle은 Queue에 쌓이는 이벤트 부담을 줄입니다. Race Condition·AbortController는 응답 순서가 요청 순서와 다를 때 중요합니다.
Node.js · 점검 · 오해
Node에도 이벤트 루프가 있어 I/O 대기 중 다른 요청을 처리할 수 있지만, 동기 CPU·동기 파일 읽기가 루프를 막으면 응답이 밀려 리버스 프록시 타임아웃·502로 이어질 수 있습니다. Event Loop Lag 모니터링이 도움이 됩니다. 클릭 지연은 Performance에서 앞선 Long Task를 찾습니다. SEO 전용 개념이 아니라 실행·반응성 이해의 기반입니다. 흔한 오해: setTimeout 1초 = 정확 1초 / async = 백그라운드 전체 / await = 브라우저 정지 / Promise는 항상 setTimeout보다 느림 / 비동기는 성능 문제 없음 — 모두 아닙니다.
자주 묻는 질문
이벤트 루프란?
현재 작업과 대기 중인 비동기 작업을 관리해 다음에 무엇을 실행할지 조정하는 JavaScript 동작 방식입니다.
Call Stack이란?
지금 실행 중인 함수가 쌓이는 구조입니다. 나중에 호출된 함수가 먼저 끝나 빠집니다(LIFO).
Task와 Microtask 차이는?
Task는 setTimeout·클릭 등, Microtask는 Promise.then 등으로 현재 Task 직후·다음 Task보다 먼저 처리되는 경우가 많습니다.
setTimeout(fn, 0)은 즉시인가요?
아닙니다. 현재 동기 코드와 Microtask 등이 처리된 뒤 실행 기회를 얻습니다.
await면 브라우저가 멈추나요?
아닙니다. 해당 async 함수만 결과를 기다리고, 다른 이벤트·작업은 실행될 수 있습니다.
INP와 관계가 있나요?
네. 메인 스레드가 긴 작업에 막히면 클릭 이벤트가 Queue에서 기다려 반응성이 나빠질 수 있습니다.
정리
이벤트 루프는 JavaScript가 여러 작업을 어떤 순서로 실행할지 관리하는 핵심 방식입니다. 현재 작업을 먼저 끝내고, 대기 작업은 Queue에서 기다리며, Stack이 비면 다음을 올립니다. Promise 같은 Microtask는 일반 다음 Task보다 먼저 처리될 수 있습니다. setTimeout·Promise·async/await·클릭·Long Task·INP·TBT가 여기서 연결됩니다. 그런데 Promise.then()이 setTimeout(..., 0)보다 먼저인 경우가 많은 이유는 대기열이 하나가 아니라 Task와 Microtask로 나뉘기 때문입니다. 다음에는 마이크로태스크(Microtask)가 Task와 어떻게 다른지 알면 실행 순서를 더 정확히 이해할 수 있습니다.