1. JavaScript는 단일스레드
이벤트 루프를 설명하기 전에 JavaScript의 특징을 살펴봐야 합니다. JavaScript는 단일 스레드(single-thread)로 동작합니다. 동시에 여러 작업을 수행할 수 없다는 뜻과 동일합니다. 하지만 웹 개발을 위해서는 API 통신과 같이 네트워크 요청이나 I/O 작업, 타이머 등 다양한 비동기 작업이 필요합니다.
만약 모든 I/O 작업이 동기적으로(blocking
) 수행된다면, API를 호출하거나 파일 읽기 등의 작업을 수행하는 동안 사용자의 화면이 멈춰 사용자 경험(UX)이 나빠지게 됩니다.
따라서 이벤트 루프(Event Loop
)는 이러한 I/O 작업을 백그라운드(Web APIs
)에서 처리하고, 완료된 작업을 다시 JavaScript 컨텍스트로 전달해 줍니다.
쉽게 요약하면 사용자 경험을 해치지 않고 비동기 작업들을 효율적으로 처리하기 위해 이벤트 루프라는 메커니즘을 사용합니다.
2. 이벤트 루프(Event Loop)
위에서 설명한 것과 같이 JavaScript는 비동기 작업을 효율적으로 처리하기 위해 이벤트 루프(Event Loop
)라는 메커니즘을 사용합니다.
2.1. 이벤트 루프 구성 요소
콜 스택(Call Stack)
- 함수 호출이 쌓이는 스택(Stack) 공간입니다. 현재 실행 중인 함수가 이 스택의 최상단에 쌓이며, 실행이 끝나면 스택에서 제거됩니다.
Web APIs (브라우저 환경 기준)
- setTimeout, fetch, DOM Event Listener 등 비동기 API가 동작하는 공간입니다. 이곳에서 타이머, 네트워크 요청 등이 처리되고 콜백이 준비되면 큐에 넣어 줍니다.
콜백 큐
- 콜백 큐는 우선 순위에 따라 테스크 큐, 마이크로테스크 큐로 나워집니다.

콜백 큐: 테스크 큐와 매크로테스크(Task Queue, Macrotask)
- 테스크 큐는 setTimeout, setInterval, I/O 이벤트, UI 렌더링 작업 등 큰 단위의 비동기 콜백(매크로테스크)이 대기하는 공간입니다.
콜백 큐: 마이크로테스크 큐(Microtask Queue)
- Promise.then, async/await, MutationObserver 같은 높은 우선 순위의 작업이 대기하는 곳입니다. 이벤트 루프는 매크로테스크가 끝난 직후 마이크로테스크를 모두 실행한 뒤 다음 매크로테스크로 넘어갑니다.
- 우선 순위: 마이크로테스크 > 매크로테스크

이벤트 루프(Event Loop)
- 콜 스택이 비어 있으면 테스크 큐나 마이크로테스크 큐에서 대기 중인 콜백을 꺼내 콜 스택에 푸시(push)하고 실행합니다. 이 과정을 무한히 반복하면서 비동기 처리를 관리합니다.
2.2. 이벤트 루프 동작 순서
- 콜 스택 확인
- 콜 스택이 비어 있는지 확인합니다. 비어 있지 않으면 스택이 빌 때까지 기다립니다.
- 마이크로테스크 실행
- 콜 스택이 비면, 먼저 마이크로테스크 큐를 확인하고 모두 실행합니다.
- 매크로테스크 실행
- 마이크로테스크 큐가 비면, 테스크 큐에서 매크로테스크를 꺼내 실행합니다.
- 화면 업데이트
- 브라우저는 일정 시점마다 렌더링을 수행합니다. 이 과정은 브라우저 구현마다 다르지만, 보통 매크로테스크와 매크로테스크 사이 혹은 마이크로테스크 이후에 실행됩니다.
- 반복
- 위 과정을 1번으로 돌아가 다시 반복합니다.
2.3. 코드 예시
console.log('시작');
setTimeout(() => {
console.log('매크로테스크: setTimeout 콜백');
}, 0);
Promise.resolve()
.then(() => {
console.log('마이크로테스크: Promise.then 1');
})
.then(() => {
console.log('마이크로테스크: Promise.then 2');
});
console.log('끝');

실행 결과를 보면, 코드 위치에서 매크로테스크의 콜백이 마이크로테스크 콜백보다 먼저 정의되어 있습니다. 하지만, 마이크로테스크가 우선순위가 더 높기 때문에 먼저 모두 실행됩니다. 그 이후 매크로테스크가 실행됩니다.
[실행 순서 설명]
-
console.log('시작')
: 즉시 실행 -
setTimeout(..., 0)
: Web APIs로 전달, 테스크 큐에 콜백 대기 -
Promise.resolve().then(...)
: 즉시 마이크로태스크 큐에 첫 번째 콜백 등록 -
console.log('끝')
: 즉시 실행 -
콜 스택이 비면, 마이크로태스크 먼저 모두 실행
-
Promise.then 1
출력 -
Promise.then 2
출력
-
-
마이크로태스크 큐가 비면, 매크로태스크 실행
setTimeout
콜백 실행