자바스크립트는 싱글 스레드 언어인데, 어떻게 동시에 여러 작업들을 수행하나요?
자바스크립트는 싱글 스레드로 한번에 한 작업씩 진행할 수 있지만 내부적인 동작을 통해서 마치 멀티 스레드처럼 여러가지 작업을 한번에 진행하는것 처럼 보인다. 여기에서 자주 쓰이는 방식이 문맥 교환으로 컨텍스트 스위칭이다. 내부적으로 들어오는 작업에 대해서 분류하고 해당 작업들을 스위칭해가며 진행함으로서 한번에 진행되는 느낌을 준다.
내부적으로 이벤트큐와 마이크로큐와 같은 방식으로 settime 함수등 분류된 작업을 두고 차등순서를 두어서 처리하는걸로 알고 있다.
해당 작업에 대해서는 명확히 기억나지는 않음
자바스크립트는 싱글 스레드 언어이다. 단일 콜 스택을 가지지만, 브라우저나 Node.js 환경이 제공하는 비동기 처리 메커니즘 덕분에 여러 작업을 수행할 수 있다.
브라우저의 WebApi 나 Node의 libuv, 이벤트 루프, 태스크 큐를 이용하여 비동기 작업을 동시에 처리한다.
비동기 작업이 발생하면, 해당 작업은 브라우저의 Web API 에 위임된다. setTimeout 이나 fetch 와 같은 작업이 수행되면 자바스크립트 엔진은 이 작업들을 Web API 에 넘기고 다른 코드 실행을 진행한다. Web API 에서 비동기 작업이 완료되면, 그 작업은 태스크 큐에 들어가서 대기한다.
이후 이벤트 루프가 콜 스택이 비어있는지 확인한 뒤 태스크 큐에서 대기중인 작업을 콜 스택으로 가져와 실행한다. 이러한 구조로 UI 인터랙션이 끊기지 않으며 대기 시간이 필요한 작업도 동시에 실행되는 것과 같이 동작하게 된다.
매크로태스크 큐
마이크로태스트 큐
비동기 작업의 우선순위를 관리하고, 이벤트 루프가 적절한 시점에 콜백을 실행하기 위해 사용된다.
매크로태스크 큐는 일반적인 비동기 작업의 콜백이 저장되는 큐이다.
setTimeout, setInterval, I/O작업, 이벤트 핸들러 등은 작업 완료 후 매크로태스크 큐에 콜백을 대기시킨다. 매크로태스크 큐는 이벤트 루프의 한 번의 반복마다 하나의 태스크만 처리되기 때문에 UI 업데이트나 다른 작업과 균형 있게 진행된다
마이크로태스크 큐 더 높은 우선순위가 필요한 비동기 작업이 대기하는 큐
Promise.then, MutationObserver 등의 비동기 콜백이 저장된다. 이벤트 루프는 매크로태스크를 실행하기 전에 항상 마이크로태스크 큐를 먼저 확인하고, 모든 마이크로태스크를 처리한 후 매크로태스크로 넘어간다.
GPT-5 를 이용한 분석
전체적인 큰 흐름은 맞는데 일부 개념이 섞여서 헷갈린 부분이 있어요.
특히 문맥 교환(컨텍스트 스위칭) 개념과 이벤트 루프의 태스크 큐 처리 방식이 혼동된 것으로 보입니다.
문맥 교환과 이벤트 루프의 차이
이벤트 큐 vs 태스크 큐
자바스크립트 엔진 (싱글 스레드)
├── Call Stack (실행 컨텍스트)
└── Memory Heap (메모리 할당)
브라우저/Node.js 환경 (멀티 스레드)
├── Web APIs (setTimeout, fetch 등)
├── Task Queue (매크로태스크)
├── Microtask Queue (마이크로태스크)
└── Event Loop (조정자 역할)
환경에 대한 이해를 명확히 해야 함.
해당 웹사이트에서 이벤트 루프 시각화를 확인 가능
웹 API 의 종류
├── Web APIs (브라우저가 제공)
│ ├── DOM API (document.querySelector)
│ ├── fetch API
│ ├── setTimeout/setInterval
│ ├── localStorage
│ └── Canvas, WebGL, Audio API 등
몇가지 예시
// 이 코드를 실행할 때 실제로 일어나는 일
console.log('시작'); // Main Thread에서 실행
// fetch를 호출하면...
fetch('https://api.example.com/data')
// 1. Main Thread: fetch 호출 등록
// 2. Network Thread: 실제 HTTP 요청 수행 (별도 스레드!)
.then(data => {
// 3. 요청 완료 후 콜백이 Task Queue로
// 4. Event Loop가 Main Thread로 가져와 실행
console.log('데이터 받음');
});
setTimeout(() => {
// Timer Thread가 시간 측정 (별도 스레드!)
console.log('타이머 완료');
}, 1000);
console.log('끝'); // Main Thread에서 즉시 실행
// 실행 순서 예제
console.log('1'); // 동기
setTimeout(() => console.log('2'), 0); // 매크로태스크
Promise.resolve().then(() => console.log('3')); // 마이크로태스크
console.log('4'); // 동기
// 출력: 1, 4, 3, 2