자바스크립트의 비동기 처리와 이벤트 루프(Event Loop) 이해하기

자바스크립트는 싱글 쓰레드(single-threaded) 기반 언어입니다. 이는 한 번에 하나의 작업만 처리할 수 있음을 의미합니다. 그러나 자바스크립트가 웹 애플리케이션에서 빠른 성능을 보이는 이유는 무엇일까요? 바로 이벤트 루프(Event Loop)와 비동기 처리 덕분입니다. 이 글에서는 자바스크립트의 비동기 처리 메커니즘과 이벤트 루프의 역할을 살펴보겠습니다.

 

콜 스택(Call Stack)과 힙(Heap)

자바스크립트의 실행 컨텍스트는 크게 콜 스택(Call Stack)과 힙(Heap)으로 구성됩니다. 콜 스택은 함수의 호출을 기록하는 구조로, 함수가 호출되면 콜 스택에 추가(push)되고, 함수의 실행이 완료되면 콜 스택에서 제거(pop)됩니다. 반면, 힙은 복잡한 객체와 같은 동적으로 할당된 메모리를 저장하는 영역입니다.

JS의 Evnet Loop를 통한 동시성 지원
JS의 Evnet Loop를 통한 동시성 지원

function multiply(a, b) {
  return a * b;
}

function square(n) {
  return multiply(n, n);
}

function printSquare(n) {
  var squared = square(n);
  console.log(squared);
}

printSquare(4);

위 코드를 실행하면, printSquaresquaremultiply 순으로 콜 스택에 쌓이고, 함수가 반환되면서 콜 스택에서 제거됩니다.

 

비동기 처리와 Web APIs

자바스크립트는 싱글 쓰레드이지만, 비동기 처리를 통해 동시성을 구현합니다. 비동기 함수(예: setTimeout, fetch, Promise, XMLHttpRequest 등)는 Web APIs를 호출하며, 해당 작업이 완료되면 콜백 함수를 콜백 큐에 추가합니다.

console.log(1);
setTimeout(() => {
    console.log(2);
}, 0);
console.log(3);

 

위 코드는 1, 3, 2 순서로 출력됩니다. setTimeout 함수는 Web API를 통해 처리되고, 지정된 시간(여기서는 0초)이 지나면 콜백 함수를 태스크 큐에 추가합니다.

 

콜백 큐(Callback Queue)

Callback Queue
Callback Queue

 

비동기 작업이 완료되면, 그에 해당하는 콜백 함수가 콜백 큐에 추가됩니다. 콜백 큐는 FIFO(First In, First Out) 구조로, 가장 먼저 들어온 콜백 함수가 가장 먼저 처리됩니다.

 

모든 비동기 코드가 같은 Queue에 쌓이는 것이 아니라 Call Queue에는 세 종류가 있습니다.

  1. (Macro)Task Queue : setTimeout, setInterval 등
  2. Microtask Queue : Promise, async 와 같은 코드 등
  3. Animation Frames : requestAnimationFrame과 같은 코드 등

Microtask Queue > Animation Frame > Task Queue 순으로, Microtask Queue가 가장 먼저 실행되고 Task Queue가 가장 늦게 실행됩니다.

 

이벤트 루프(Event Loop)

이벤트 루프의 주 역할은 콜 스택과 콜백 큐를 모니터링하는 것입니다. 콜 스택이 비어 있고 콜백 큐에 대기 중인 함수가 있다면, 이벤트 루프는 콜백 큐에서 함수를 하나씩 콜 스택으로 이동시킵니다. 이 과정을 통해 비동기 콜백 함수들이 적절한 시점에 실행될 수 있도록 합니다.

console.log('Start');

setTimeout(() => {
    console.log('Timeout');
}, 0);

Promise.resolve('Promise').then(console.log);

console.log('End');

 

위 코드를 실행시켰을 때, console.log('Start'); → console.log('End') → setTimeout → Promise 순으로 callStack에 들어가게 됩니다. 하지만, Start와 End는 call stack에서 바로 나와 콘솔에 출력 되고, 비동기 함수인 setTimeout과 Promise는 Web API를 통해 각각 microTask Queue와 Task Queue에 들어가게 됩니다. ‘End’가 출력 되면서 Call Stack이 비워졌을 때 우선순위가 높은 Promise가 먼저 Call Stack 으로 이동해 ‘Promise’ → ‘Timeout’ 순서로 콘솔에 출력이 됩니다.

 

이처럼 이벤트 루프는 자바스크립트의 비동기 동작을 가능하게 하는 핵심 메커니즘입니다. 단일 쓰레드인 자바스크립트가 동시성을 지원할 수 있는 것은 이벤트 루프 덕분입니다. 이벤트 루프의 이해는 자바스크립트의 비동기 처리 패턴을 깊게 이해하고, 더 효율적인 코드를 작성하는 데 필수적입니다.