HomeBlog
Back to Blog

Contents

    Copyright 2025. Kwangmin. All rights reserved.
    2025년 2월 26일조회수:

    JS Event Loop 해치워버리기

    개요

    자바스크립트는 싱글 스레드(single-threaded) 언어입니다. 즉, 한 번에 한 작업만 수행할 수 있는데요. 그럼에도 불구하고, 네트워크 요청, 타이머, I/O 작업 등 오래 걸리는 작업을 효과적으로 처리할 수 있는 이유는 바로 이벤트 루프(event loop) 덕분입니다. 이번 포스트에서는 자바스크립트 엔진 내부의 작동 원리와 함께, 어떻게 동기와 비동기 작업을 구분하여 처리하는지 자세히 알아보겠습니다.


    1. 싱글 스레드와 동기 실행

    자바스크립트의 기본 실행 환경은 한 번에 하나의 작업만 처리하는 싱글 스레드입니다.

    • 동기 코드: 함수가 호출되면 호출 순서대로 바로 실행됩니다.
    • 문제점: 만약 오래 걸리는 작업(예: 복잡한 계산, 대량의 데이터 처리 등 , 서버 요청 등)을 동기적으로 처리한다면, 해당 작업이 끝날 때까지 다른 코드가 실행되지 않아 사용자 경험(UX)이 크게 저하됩니다.
    • 이 문제를 해결하기 위해 자바스크립트는 비동기 작업을 위한 구조를 도입하였습니다.

    1.2 싱글스레드의 주요 특징

    • 단일 호출 스택: 자바스크립트 엔진은 하나의 호출 스택을 사용하여 함수의 호출과 실행 순서를 추적합니다. 호출 스택은 함수가 호출될 때 해당 함수의 실행 컨텍스트를 쌓고(pop) 실행이 완료되면 제거합니다. 이를 통해 함수 실행 순서를 관리합니다.

    • 차단(Blocked) 방지: 싱글스레드 언어로, 한 번에 하나의 작업만 처리할 수 있기 때문에 작업이 오래 걸리면 다른 작업들이 차단될 수 있습니다. 따라서 긴 작업이 실행되면 브라우저나 애플리케이션의 응답성이 저하될 수 있습니다. 이를 방지하기 위해 자바스크립트는 비동기적인 처리 방식을 사용합니다.

    • 비동기 처리: 자바스크립트는 이벤트 루프(Event Loop)와 비동기 콜백 함수를 통해 비차단(non-blocking) 실행 방식을 지원합니다. 비동기 작업은 웹 API와 같은 환경 제공자에 의해 처리되며, 작업이 완료되면 콜백 함수가 호출 스택으로 이동하여 실행됩니다. 이를 통해 다른 작업들을 기다리지 않고 다음 작업을 처리할 수 있습니다.

    • 동기적 실행: 싱글스레드이기 때문에 코드는 순차적으로 실행됩니다. 동기적으로 실행되는 코드는 작업이 완료될 때까지 다음 작업으로 넘어가지 않습니다. 이는 코드의 실행 순서와 의도한 순서를 일치시키는데 도움을 줍니다. 싱글스레드로 동작하는 자바스크립트는 비동기 처리를 통해 작업의 차단을 방지하고, 이벤트 루프를 통해 비동기 작업을 관리하여 동시성을 제공합니다. 이를 통해 자바스크립트는 더 나은 응답성과 사용자 경험을 제공할 수 있습니다.


    2. 콜 스택 (Call Stack)

    자바스크립트 엔진은 콜 스택이라는 자료구조를 사용하여 현재 실행 중인 함수들을 관리합니다. 콜 스택은 실행할 함수가 쌓이는 LIFO(Last In, First Out) 구조의 자료구조입니다.

    • 동작 방식: LIFO(Last In, First Out) 구조로, 함수가 호출되면 스택에 추가되고, 함수 실행이 완료되면 스택에서 제거됩니다.

    ✅ 콜 스택의 동작 방식

    1. 실행할 함수가 호출되면 스택에 추가(push)된다.
    2. 함수 실행이 끝나면 스택에서 제거(pop)된다.
    3. 스택이 비어야 다음 작업이 실행될 수 있다.
    • 예시:
    function first() {
      console.log("첫 번째 함수 실행");
      second();
    }
     
    function second() {
      console.log("두 번째 함수 실행");
    }
     
    first();
     

    실행 과정

    1. first() 함수가 콜 스택에 들어감
    2. console.log("첫 번째 함수 실행") 실행 후 second() 호출
    3. second()가 스택에 추가됨
    4. console.log("두 번째 함수 실행") 실행 후 second()가 스택에서 제거됨
    5. first() 함수 실행이 끝나면 스택에서 제거됨

    콜 스택만으로 실행된다면, 비동기 처리는 불가능합니다. 여기서 Web API가 등장합니다.


    3. Web API와 비동기 작업

    자바스크립트는 브라우저 환경이나 Node.js와 같은 런타임에서 Web API(또는 내부 API)를 제공합니다.

    • Web API의 역할:
      • 브라우저 내장 기능(ex. setTimeout, DOM 이벤트, XMLHttpRequest, fetch 등)을 통해 오래 걸리는 작업을 별도의 환경(보통 브라우저의 백그라운드 스레드)에 위임합니다.
      • 이러한 작업은 자바스크립트의 메인 스레드(콜 스택)를 차단하지 않고 동시에 진행됩니다.
    • 비동기 작업 처리 과정:
      • 비동기 작업 요청: 예를 들어, setTimeout이 호출되면 콜 스택에는 해당 함수의 호출이 추가되지만, 즉시 Web API로 위임되어 타이머가 동작합니다.
      • 콜 스택 비우기: 동기 코드는 계속 실행되며, 콜 스택이 비워지면 Web API에서 완료된 작업의 콜백이 큐에 저장됩니다.
      • 이러한 구조 덕분에 자바스크립트는 메인 스레드를 블로킹하지 않으면서 동시에 여러 작업을 효율적으로 처리할 수 있습니다.

    4. 마이크로태스크 큐 (Microtask Queue)와 이벤트 큐 (Task Queue)

    이벤트 큐 (Task Queue)

    • 대상: setTimeout, DOM 이벤트 핸들러, setInterval 등 Web API에서 완료된 일반 비동기 작업의 콜백.
    • 특징: 콜 스택이 완전히 비워진 후에, 이벤트 큐에 대기 중인 콜백을 하나씩 콜 스택으로 옮겨 실행합니다.
    console.log("1. 코드 시작");
     
    setTimeout(() => {
      console.log("2. setTimeout 실행");
    }, 1000);
     
    console.log("3. 코드 끝");
    1. "1. 코드 시작" → 즉시 실행
    2. setTimeout 호출 → Web API로 이동 (콜백은 이벤트 큐에 저장)
    3. "3. 코드 끝" → 즉시 실행
    4. 이벤트 루프가 콜스택이 비어있음을 확인하고 1초 후 → 콜 스택이 비어 있으면 이벤트 큐의 콜백 실행

    setTimeout이 0초여도 바로 실행되지 않고 이벤트 큐에서 대기하는 이유가 여기에 있습니다.


    마이크로태스크 큐 (Microtask Queue)

    • 대상: Promise의 then, catch, finally 그리고 MutationObserver와 같이 상대적으로 우선 순위가 높은 비동기 작업.
    • 우선 처리: 콜 스택이 비워진 후, 이벤트 큐의 콜백보다 먼저 마이크로태스크 큐에 있는 작업들을 모두 처리합니다.
    console.log("1. 코드 시작");
     
    setTimeout(() => {
      console.log("3. setTimeout 실행");
    }, 0);
     
    Promise.resolve().then(() => {
      console.log("2. Promise 실행");
    });
     
    console.log("4. 코드 끝");
    1. "1. 코드 시작" → 즉시 실행
    2. setTimeout 호출 → Web API로 이동 (콜백은 이벤트 큐에 저장)
    3. Promise.then() → 마이크로태스크 큐에 추가
    4. "4. 코드 끝" → 동기 실행
    5. 이벤트 루프가 콜스택이 비어있음을 확인하고 → 마이크로태스크 큐 실행 → "2. Promise 실행"
    6. 이벤트 큐 실행 → "3. setTimeout 실행"

    마이크로태스크가 먼저 실행되므로 setTimeout보다 Promise.then()이 먼저 실행됩니다.

    이처럼 두 큐는 서로 다른 우선 순위를 가지고 있으며, 이는 비동기 작업의 실행 순서를 결정하는 중요한 역할을 합니다.


    5. 정리

    5.1. 스레드 (Thread)

    • 정의: 스레드는 프로그램 내에서 동시에 실행되는 작업의 최소 단위입니다.
    • 자바스크립트의 특징: 자바스크립트는 싱글 스레드 언어로, 하나의 메인 스레드에서 코드를 순차적으로 실행합니다. 그러나 브라우저나 Node.js와 같은 실행 환경은 내부적으로 여러 스레드를 사용하여, 타이머, 네트워크 요청, 렌더링, I/O 등 비동기 작업을 별도의 백그라운드 스레드에서 처리합니다.
    • 비동기 처리: 메인 스레드가 직접 비동기 작업을 실행하지 않고, Web API나 OS 수준의 스레드가 작업을 수행한 후 결과를 이벤트 루프를 통해 메인 스레드로 전달합니다.

    5.2. 콜 스택 (Call Stack)

    • 정의: 콜 스택은 함수 호출과 실행 컨텍스트를 관리하는 자료구조입니다.
    • 동작 방식: 함수 호출 시 해당 함수의 실행 컨텍스트가 스택에 푸시(push) 됩니다. 함수 실행이 완료되면 스택에서 팝(pop) 되어 제거됩니다.
    • 특징: LIFO(Last-In-First-Out) 구조로, 마지막에 들어온 함수가 가장 먼저 실행되고 종료됩니다.

    5.3. 힙 (Heap)

    • 정의: 힙은 동적으로 할당된 메모리 영역입니다.
    • 용도: 객체, 배열, 함수와 같은 동적 데이터가 저장되는 곳이며, 가비지 컬렉션에 의해 관리됩니다.
    • 특징: 콜 스택과 달리 구조화되지 않은 메모리 영역으로, 메모리의 할당과 해제가 자유롭게 이루어집니다.

    5.4. 이벤트 루프 (Event Loop)

    • 정의: 이벤트 루프는 자바스크립트 엔진에서 비동기 작업을 관리하고 처리하는 핵심 메커니즘입니다.
    • 역할: 콜 스택 감시: 콜 스택이 비어 있는지 지속적으로 확인합니다. 비동기 작업 처리: 콜 스택이 비면, 대기 중인 비동기 작업(마이크로태스크와 이벤트 큐에 있는 콜백)을 차례로 실행합니다. 의의: 이벤트 루프는 자바스크립트가 싱글 스레드임에도 불구하고, 비동기 작업을 통해 멀티스레드 환경과 유사한 효과를 낼 수 있도록 해줍니다.

    5.5. 이벤트 큐 (Event Queue)

    • 정의: 이벤트 큐는 비동기 작업의 콜백 함수들이 대기하는 FIFO(First-In-First-Out) 구조의 큐입니다.
    • 동작 방식: Web API를 통해 처리된 비동기 작업의 콜백이 이벤트 큐에 저장됩니다. 콜 스택이 비면, 이벤트 루프가 이벤트 큐에서 첫 번째 콜백을 꺼내어 실행합니다. 예시: setTimeout, 사용자 이벤트, DOM 이벤트 등이 이벤트 큐에 등록됩니다.

    5.6. 마이크로태스크 큐 (Microtask Queue)

    • 정의: 마이크로태스크 큐는 Promise의 then, catch, finally 콜백과 MutationObserver와 같이 비교적 우선순위가 높은 비동기 작업들이 대기하는 큐입니다. 특징:
    • 우선 순위: 마이크로태스크 큐에 있는 작업은 콜 스택이 비었을 때, 이벤트 큐의 작업보다 먼저 실행됩니다. 즉각적 처리: 동기 코드 실행이 완료된 후, 즉시 모든 마이크로태스크가 순차적으로 실행됩니다.
    • 동작 방식:
    1. 동기 코드 실행 완료: 콜 스택이 완전히 비워지면 이벤트 루프는 먼저 마이크로태스크 큐를 확인합니다.
    2. 모든 작업 처리: 큐에 있는 모든 마이크로태스크(예: Promise의 then 콜백)가 실행되며, 이 중간에 새 작업이 추가되더라도 큐가 완전히 비워질 때까지 처리됩니다.
    3. 이벤트 큐로 전환: 마이크로태스크 큐가 완전히 처리된 후, 이벤트 큐에 있는 작업들이 실행됩니다.

    6. Visualization & Reference

    개념이 잘 이해가안가거나 헷갈리신다면 해당 유튜브 에서 이벤트 루프를 시각화한 영상을 볼 수 있습니다.

    Tags
    javascript
    AWS S3 멀티파트 업로드
    SSR의 한계를 극복해줄 RSC Suspense Streaming

    댓글 로딩 중...

    조회수:0