
자바스크립트를 막 배운 입문자라면 한 번쯤은 이런 경험이 있을 것이다.
setTimeout을 썼는데 내가 생각한 순서대로 실행되지 않거나,Promise가 예상과 다르게 동작해서 혼란스러웠던 순간. 이럴 때마다 드는 생각은 하나다."왜 자바스크립트는 내가 쓴 코드 순서대로 동작하지 않을까?"
이 질문에 답하려면 자바스크립트의 이벤트 루프(Event Loop)와 태스크 큐(Task Queue)에 대한 이해가 필요하다. 이 글에서는 한 명이 운영하는 카페의 하루를 예로 들어 자바스크립트가 어떻게 멈추지 않고 여러 작업을 동시에(?) 처리하는 것처럼 보일 수 있는지 풀어보려 한다.
자바스크립트는 기본적으로 싱글 스레드 언어다. 즉, 한 번에 한 줄의 코드만 실행할 수 있다.
그런데 setTimeout, fetch, Promise 같은 것들이 동시에 처리되는 것처럼 보이는 이유는 뭘까?
그 이유는 자바스크립트가 혼자 일하는 게 아니라, 브라우저가 옆에서 도와주고 있기 때문이다.
이것을 카페 알바에 비유해서 생각해보자 💬
혼자서 카페를 운영하는 바리스타 자바🙋♀️가 있다고 상상해보자.
손님이 오면
1. 주문을 받고 → 2. 커피를 내리고 → 3. 손님에게 전달
하지만 이렇게 하면 커피 한 잔을 내리는 3분 동안 다른 손님들은 계속 기다려야 한다.
손님이 많아질수록 대기시간은 길어질 것이다.
그래서 자바는 다음과 같은 시스템을 만들었다.
1. 주문 접수대 (콜 스택)
2. 자동 커피머신 (Web APIs)
3. 완성 대기 구역 (태스크 큐)
4. 우선 처리함 (마이크로태스크 큐)
자바의 하루는 아래와 같은 패턴으로 반복된다.
이렇게 하면 커피를 내리는 동안에도 다른 손님의 주문을 받을 수 있고, 완성된 음료는 순서대로 전달할 수 있다.
위에서 한 카페 비유를 실제 자바스크립트 환경으로 옮겨보면 아래와 같다.
| 카페 시스템 | 자바스크립트 환경 | 역할 |
|---|---|---|
| 주문 접수대 | 콜 스택(Call Stack) | 현재 실행중인 함수가 쌓이는 스택 |
| 자동 커피머신 | Web APIs | setTimeout, fetch, Promise 등의 비동기 작업 처리 |
| 완성 대기 구역 | 태스크 큐(Task Queue) | Web API 작업 완료 후 콜백이 대기하는 곳 |
| 우선 처리함 | 마이크로태스크 큐 | Promise.then 등 우선 순위가 높은 작업 대기 |
| 바리스타 자바 | 이벤트 루프 | 각 큐를 확인하며 작업을 순서대로 처리 |
이벤트 루프는 말 그대로 반복(loop)하면서 다음과 같은 일을 한다.
1. 콜 스택이 비어있는지 확인한다.
2. 비어있다면 마이크로태스크 큐가 비어있는지 확인한다.
3. 마이크로태스크가 있다면 그것부터 처리한다.
4. 이후 태스크 큐에 있는 작업을 하나 꺼내 실행한다.
즉 이벤트 루프는 "콜 스택이 비면, 대기 중인 작업들을 하나씩 처리해주는 관리자" 역할을 한다.
console.log('손님1: 주문 접수'); // 즉시 처리
setTimeout(() => {
console.log('손님2: 아메리카노 완성'); // 커피머신에서 3초 후 완성
}, 3000);
Promise.resolve().then(() => {
console.log('VIP손님: 우선 처리'); // 우선 처리함으로 바로 이동
});
console.log('손님3: 주문 접수'); // 즉시 처리
위 코드의 출력 결과는 아래와 같다.
손님1: 주문 접수
손님3: 주문 접수
VIP손님: 우선 처리
손님2: 아메리카노 완성
console.log('손님1') → 콜 스택에서 즉시 실행setTimeout(...) → Web API(커피머신)에 위임Promise.then(...) → 마이크로태스크 큐(우선 처리함)에 저장console.log('손님3') → 콜 스택에서 즉시 실행console.log('VIP손님') 실행console.log('손님2') 실행자바스크립트는 싱글 스레드지만, 브라우저의 도움을 받아 마치 여러 일을 동시에 하는 것처럼 보인다.
핵심은 기다려야 하는 일은 다른 곳에 맡기고, 지금 당장 할 수 있는 일부터 처리한다는 것이다.
처음에는 이런 흐름이 복잡하게 느껴졌지만, 이 개념을 이해하며 async/await, fetch, 이벤트 리스너 등의 동작 원리도 점차 자연스럽게 이해할 수 있게 됐다.
자바스크립트와 친해지기는 계속된다 . .. . 🤞