왜 JavaScript의 명령어 처리 동작 원리를 이해해야 할까?
2024. 12. 23. 23:24

JavaScript를 쓰다 보면, 코드 실행 순서가 예상과 다르게 흘러가는 경우가 자주 있다.
특히 setTimeout, Promise, fetch 같은 비동기 작업을 사용할 때 더욱 그렇다.

 

예를 들어 아래 코드를 보자.

console.log('Start');

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

Promise.resolve().then(() => {
  console.log('Microtask');
});

requestAnimationFrame(() => {
  console.log('Animation Frame');
});

console.log('End');

코드를 읽는 순서대로 실행될 것 같지만 실제 출력은 다음과 같다.

Start
End
Microtask
Animation Frame
Task

이처럼 예상과 다른 실행 순서는 JavaScript의 명령어 처리 구조와 관련이 있다.

[ 위 결과는 환경에 따라 달라질 수 있다.

일부 문서에서는 Animation FrameTask보다 먼저 실행된다고 설명되지만,

브라우저 렌더링 타이밍에 따라 Task가 먼저 실행되는 경우도 있다. ]


명령어 처리의 핵심 구성요소

1. Call Stack (호출 스택)

  • 현재 실행 중인 함수나 작업을 저장하는 LIFO 구조의 스택
  • 함수가 호출되면 위로 쌓이고(push), 실행이 끝나면 제거(pop)
  • 만약 무거운 연산(예: 무한 반복문)이 호출 스택을 계속 차지하면, 비동기 작업조차 실행되지 않음 (이벤트 루프가 콜백을 가져올 수 없기 때문)

 

2. Web APIs

  • 브라우저는 setTimeout, DOM 이벤트, HTTP 요청(Fetch/AJAX), requestAnimationFrame 등 다양한 비동기 작업을 처리할 수 있도록 Web APIs를 제공한다.
  • 이러한 작업들은 **자바스크립트 호출 스택(Call Stack)**만으로는 직접 처리할 수 없어서, 브라우저의 Web APIs가 대신 처리한다.
  • 예를 들어:
    • setTimeout: 일정 시간이 지난 뒤 콜백 실행
    • DOM 이벤트: 사용자 입력 이벤트 처리
    • HTTP 요청: 서버와 통신 후 결과 수신
    • requestAnimationFrame: 다음 프레임에 콜백 실행
  • Web APIs가 작업을 완료하면, 콜백을 Callback Queue에 등록한다.

 

3. Callback Queue

  • Web APIs가 완료한 작업의 콜백, 그리고 프라미스와 같은 비동기 콜백이 큐에 쌓인다.
  • 콜백의 종류에 따라 처리 시점과 우선순위가 다르다.

 

1️⃣ Microtask Queue (가장 높은 우선순위)

  • 예: Promise.then, queueMicrotask, MutationObserver
  • 현재 실행 중인 작업(Call Stack)이 끝나면 가장 먼저 처리
  • 한 이벤트 루프 틱(턴)에서 Microtask Queue가 비워질 때까지 모두 처리한 후 다음 단계로 넘어감

 

2️⃣ Task Queue (Macro Task Queue)

  • 예: setTimeout, setInterval, I/O, fetch
  • Microtask Queue가 완전히 비워진 후에, 이벤트 루프가 하나씩 꺼내서 처리

 

3️⃣ Animation Frame Callbacks

  • 예: requestAnimationFrame(callback)
  • 정확히는 큐라기보다는 브라우저의 렌더링 스케줄에 따라 예약된 콜백 목록
  • 보통 Microtask가 끝나고, 다음 Task가 실행되기 전에 렌더링 준비를 위해 실행
  • 다만 브라우저의 렌더링 타이밍에 따라 실행 시점은 유동적일 수 있음

 

4. Event Loop

  • 호출 스택이 비었는지 확인
  • 비어 있다면, 다음 순서로 콜백을 실행함:
    1. Microtask Queue의 모든 작업 실행
    2. (렌더링 타이밍이라면) requestAnimationFrame 콜백 실행
    3. Task Queue에서 하나의 작업 실행
  • 이 과정을 매우 빠르게 반복하며 프로그램을 실행함

실행함: Call Stack으로 옮겨져 처리

이해를 돕는 애니메이션


그래서 이걸 왜 알아야 할까?

비동기 코드는 단순히 "나중에 실행된다"고만 생각하면 곤란하다.

작업 우선순위, 호출 스택 상태, 이벤트 루프 구조까지 이해하고 있어야 비동기 동작을 제대로 예측하고 문제 상황에 대처할 수 있다.

 

예를 들어:

  • 호출 스택에 무거운 작업이 쌓이면 비동기 콜백이 큐에 있어도 실행되지 않는다.
  • setTimeout보다 먼저 작성된 Promise.then이 먼저 실행되는 이유는 Microtask Queue가 Task Queue보다 우선이기 때문이다.
  • 이 구조를 이해하지 못하면 UI가 멈추거나 이벤트가 반응하지 않는 이유를 파악하기 어렵다.

요약

구성 요소 설명
Call Stack 현재 실행 중인 함수의 저장 공간 (LIFO)
Web APIs 비동기 작업을 처리하고 콜백을 큐로 전달
Callback Queues 실행 대기 중인 콜백 함수들이 대기하는 공간
Event Loop 호출 스택이 비면 큐에서 작업을 꺼내 실행함

 

 

애니메이션 출처

https://github.com/Esoolgnah/Frontend-Interview-Questions/blob/main/Notes/important-4/event-loop.md

 

참고하면 좋을 자료

https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model

 

'FE > JS' 카테고리의 다른 글

JavaScript - this  (0) 2025.05.19
자바스크립트 실행 컨텍스트  (0) 2025.05.19
자바스크립트 호이스팅 정리  (1) 2025.05.18
null, undefined, undeclared, NaN  (1) 2025.05.17
JavaScript: var, let, const  (0) 2025.05.13