자바스크립트의 비밀스러운 삶: Promise (Microtasks)

발행: (2026년 2월 3일 오후 01:17 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

VIP 라인 이해하기: 마이크로태스크 vs. 매크로태스크

Timothy는 작은 도서관 테이블에 앉아 두 장의 종이를 뒤섞고 있었다. Margaret가 다가오자 그는 이마에 찡그린 표정을 지었다.

“이해가 안 돼요,” 그가 조용히 말했다. “코드에 0밀리초를 기다리라고 했어요. 0이요. 그럼 즉시 실행돼야 하지 않나요?”

그는 테이블 위에 코드 조각을 밀어넣었다.

console.log("1. Start");

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

Promise.resolve().then(() => {
    console.log("3. Promise");
});

console.log("4. End");

“1, 2, 3, 4가 나올 거라고 기대했어요,” Timothy가 설명했다. “아니면 1, 4, 2, 3일 수도 있겠고. 그런데 실제로는 이렇게 나왔어요.”

1. Start
4. End
3. Promise
2. Timeout

“볼륨 6에서 이벤트 루프에 대해 얘기했을 때 기억나요? 우리는 웨이터가 큐를 확인한다고 했었죠.”

“맞아요,” Timothy가 고개를 끄덕였다.

“그런데,” Margaret가 속삭였다, “전체 이야기를 다 말해주지는 않았어요. 큐가 하나만 있는 게 아니에요. 두 개가 있거든요.”

그녀는 칠판에 큰 원을 그리고 The Event Loop라고 라벨을 붙인 뒤, 두 개의 별도 상자를 추가했다:

  • 매크로태스크 큐 – 표준 라인. setTimeout, setInterval, 사용자 상호작용 등을 담는다.
  • 마이크로태스크 큐 – VIP 라인. Promise, queueMicrotask, MutationObserver 등을 담는다.

“엔진에는 엄격한 규칙이 있어요,” 그녀가 설명했다. “현재 작업(예: 메인 스크립트)이 끝나면, 웨이터가 바로 다음 매크로태스크를 잡는 게 아니라 먼저 VIP 라인을 확인합니다.”

큐가 처리되는 방식

  1. 마이크로태스크 큐에 있는 모든 마이크로태스크를 실행한다.
  2. 그 다음에 매크로태스크 큐에서 다음 매크로태스크를 선택한다.

이 규칙 때문에 마이크로태스크는 언제나 대기 중인 매크로태스크보다 먼저 실행된다. 설령 매크로태스크의 타이머가 0 ms로 설정돼 있더라도 말이다.

실행 순서 설명

  • **console.log("1. Start")**와 **console.log("4. End")**는 호출 스택(현재 작업)에서 즉시 실행된다.
  • **setTimeout(..., 0)**은 매크로태스크 큐에 배치된다.
  • **Promise.resolve().then(...)**은 마이크로태스크 큐에 배치된다.

메인 스크립트가 끝나면 이벤트 루프는 먼저 마이크로태스크 큐를 처리해 “3. Promise”를 출력하고, 그 다음 매크로태스크 큐로 넘어가 “2. Timeout”을 출력한다.

중첩 마이크로태스크와 기아 현상

Margaret는 다음과 같은 중첩 예시로 잠재적인 함정을 보여주었다:

Promise.resolve().then(() => {
    console.log("VIP 1");
    Promise.resolve().then(() => {
        console.log("VIP 2 (Nested)");
        // 여기서 계속 Promise를 추가한다면...
    });
});

setTimeout(() => console.log("Standard Line"), 0);

이 경우:

  1. “VIP 1”이 로그된다.
  2. 중첩된 Promise.resolve().then이 마이크로태스크 큐 앞에 또 다른 마이크로태스크(“VIP 2”)를 추가한다.
  3. 이벤트 루프는 큐가 비워질 때까지 마이크로태스크 처리를 계속한다.

코드가 계속해서 새로운 마이크로태스크를 큐에 넣는다면, 마이크로태스크 큐는 절대 비워지지 않으며 이벤트 루프는 매크로태스크 큐에 도달하지 못한다. 이 상황을 기아(starvation) 라고 부른다—표준 라인(매크로태스크)이 실행 시간을 빼앗겨 브라우저가 멈출 수도 있다.

모범 사례

  • 빠르고 긴급한 업데이트가 필요하고 다음 렌더링이나 I/O 작업 전에 반드시 실행돼야 할 경우에 Promise(마이크로태스크)를 사용한다.
  • 무한히 많은 마이크로태스크를 큐에 쌓는 일을 피한다; 그렇지 않으면 매크로태스크가 기아 상태에 빠져 성능이 저하될 수 있다.

“시간 문제는 아니에요,” Timothy가 결론지었다. “상태 문제죠.”

“정확히 그렇죠,” Margaret가 답했다. “JavaScript에서 Promise는 엄숙한 약속이에요. 단순 타임아웃보다 우선순위를 갖습니다.”

Back to Blog

관련 글

더 보기 »