자바스크립트 비동기를 공부하다 보면 결국 event loop를 만나게 됩니다. Promise, setTimeout, async/await를 어느 정도 써도, 왜 어떤 코드는 바로 실행되고 어떤 코드는 나중에 실행되는지 헷갈릴 때가 많습니다.
이때 event loop 개념을 잡아두면 “자바스크립트가 한 번에 하나씩 실행되는 것 같은데도 어떻게 비동기가 가능하지?”라는 질문이 풀리기 시작합니다.
이 글에서는 아래 내용을 정리합니다.
- event loop가 무엇인지
- call stack, queue 가 어떤 역할을 하는지
- 왜 비동기 코드는 나중에 실행되는 것처럼 보이는지
핵심은 event loop는 비동기 작업 완료 후 실행할 콜백과 작업들을 적절한 시점에 call stack 으로 올려 주는 실행 조정 메커니즘이라는 점입니다.
Event Loop 를 왜 알아야 할까
자바스크립트는 기본적으로 한 시점에 하나의 실행 흐름을 따라가는 언어처럼 보입니다. 그런데도:
setTimeout- 클릭 이벤트
- 네트워크 응답 처리
- Promise 후속 처리
같은 일이 동시에 돌아가는 것처럼 보입니다.
이런 동작을 이해하려면 event loop를 알아야 합니다.
가장 기본적인 구성
입문 단계에서는 아래 세 가지를 먼저 잡으면 됩니다.
- call stack
- queue
- event loop
1. Call Stack
지금 실행 중인 함수들이 쌓이는 곳입니다. 자바스크립트는 보통 이 스택의 맨 위 함수를 실행합니다.
2. Queue
나중에 실행될 작업들이 대기하는 곳입니다. 타이머 완료, 이벤트 발생, Promise 후속 처리 같은 것들이 여기에 들어갈 수 있습니다.
3. Event Loop
call stack 이 비었는지 보고, queue 에 대기 중인 작업을 stack 으로 올리는 역할을 합니다.
즉, event loop는 “언제 다음 작업을 실행할지”를 계속 확인하는 조정자처럼 생각하면 됩니다.
간단한 예시
console.log('A');
setTimeout(() => {
console.log('B');
}, 0);
console.log('C');
많은 입문자가 A, B, C 순서를 예상하지만 실제로는 보통:
A
C
B
처럼 보입니다.
이유는 setTimeout의 콜백이 바로 stack 에 들어가는 것이 아니라, 타이머가 끝난 뒤 queue 로 가고, 그 다음 call stack 이 비었을 때 event loop 에 의해 실행되기 때문입니다.
Promise 는 왜 또 다르게 보일까
Promise.then() 이나 await 뒤의 후속 코드는 보통 일반 타이머 콜백보다 먼저 처리되는 것처럼 보일 때가 있습니다. 이 부분은 microtask/macrotask 구분까지 들어가면 더 정확해지지만, 입문 단계에서는 “queue 도 종류가 있고 우선순위가 다를 수 있다” 정도로 이해해도 충분합니다.
예를 들어:
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');
보통 결과는:
start
end
promise
timeout
처럼 보입니다.
즉, 비동기라고 다 같은 타이밍으로 실행되는 것은 아닙니다.
async/await 와 event loop
await를 만나면 함수 실행은 잠시 중단되고, 나머지 후속 흐름은 Promise 기반 비동기 처리로 이어집니다. 그래서 await 뒤 코드는 “지금 당장 stack 에서 이어서 실행”된다기보다, 나중에 이어지는 흐름으로 이해하는 편이 맞습니다.
이 점 때문에 async/await는 동기처럼 보여도 실제 실행 타이밍은 event loop 와 Promise 처리 구조에 영향을 받습니다.
자주 하는 오해
1. setTimeout(fn, 0) 이면 바로 실행된다
아닙니다. “최소한 지금 call stack 이 끝난 뒤”에 실행된다고 이해하는 편이 맞습니다.
2. event loop 가 모든 비동기 작업을 직접 실행한다
정확히는 실행 시점을 조정하고, 적절한 작업을 stack 으로 올리는 흐름을 담당한다고 보는 편이 좋습니다.
3. async/await 를 쓰면 event loop 를 몰라도 된다
가독성은 좋아지지만, 실행 순서를 이해하려면 결국 event loop 개념이 필요합니다.
처음 공부할 때 추천하는 실습
console.log,setTimeout조합 예제 실행하기- Promise 예제를 추가해서 순서 비교하기
- 같은 흐름을
async/await로 바꿔 보기 - 예상한 실행 순서와 실제 순서를 비교하기
이 과정을 직접 해 보면 event loop는 이론보다 체감으로 훨씬 빨리 이해됩니다.
FAQ
Q. event loop 는 브라우저에만 있나요?
아닙니다. Node.js 같은 런타임에서도 중요한 개념입니다.
Q. Promise 와 setTimeout 은 왜 순서가 다르게 보이나요?
대기열 종류와 처리 우선순위 차이 때문입니다.
Q. event loop 를 완벽히 알아야 비동기 코드를 쓸 수 있나요?
처음부터 완벽할 필요는 없지만, 실행 순서가 헷갈릴 때는 꼭 도움이 됩니다.
Read Next
- 비동기 문법 자체를 다시 정리하고 싶다면 Promise 와 async/await 가이드를 같이 보세요.
- 더 큰 개념부터 다시 잡고 싶다면 동기 vs 비동기 가이드가 출발점으로 좋습니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis vs RabbitMQ vs Kafka 개발자를 위한 미들웨어 트러블슈팅 허브 글입니다. Redis, RabbitMQ, Kafka 중 어떤 증상부터 먼저 봐야 하는지와 어떤 문제 패턴이 각 시스템에 가까운지 정리합니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Kafka consumer lag가 계속 늘 때: 트러블슈팅 가이드 Kafka consumer lag가 계속 늘어날 때 무엇부터 봐야 하는지 정리합니다. poll 주기, 처리 속도, rebalance, consumer 설정까지 실전 기준으로 다룹니다.
- Kafka Rebalancing Too Often 가이드 Kafka consumer group에서 rebalance가 너무 자주 일어날 때 membership flapping, poll timing, protocol, assignment churn을 어떤 순서로 봐야 하는지 설명하는 실전 가이드입니다.
- Docker container가 계속 재시작될 때: 먼저 확인할 것들 exit code, command failure, environment mistake, health check 관점에서 Docker restart loop를 푸는 실전 가이드입니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.