JavaScript를 학습하다 보면 반드시 만나게 되는 개념이 바로 Event Loop입니다.
많은 개발자들이 JavaScript를 단일 스레드 언어라고 설명하지만,
실제 웹 애플리케이션은 여러 작업이 동시에 수행되는 것처럼 동작합니다.
사용자의 클릭 이벤트를 처리하고,
서버에서 데이터를 받아오며,
이미지를 다운로드하고,
애니메이션을 실행하는 작업들이 서로 충돌하지 않고 자연스럽게 진행됩니다.
이러한 동작을 가능하게 만드는 핵심 구조가 바로 Event Loop입니다.
JavaScript 엔진 자체는 한 번에 하나의 작업만 처리할 수 있습니다.
즉 여러 작업을 동시에 수행하는 것이 아니라 매우 빠르게 작업을 전환하며 실행합니다.
Event Loop는 현재 실행 중인 작업이 끝난 뒤 다음에 어떤 작업을 실행할 것인지 결정하는 관리자 역할을 수행합니다.
이 개념을 이해하면 비동기 처리의 원리를 파악할 수 있으며,
Promise와 Async/Await가 어떻게 동작하는지 이해하는 데도 큰 도움이 됩니다.
JavaScript가 단일 스레드인 이유
JavaScript는 처음부터 웹 브라우저에서 동작하기 위해 설계되었습니다.
브라우저 화면을 제어하는 과정에서 여러 스레드가 동시에 DOM을 수정한다면
예상하지 못한 충돌과 오류가 발생할 수 있습니다.
이를 방지하기 위해 JavaScript는 기본적으로 단일 스레드 구조를 채택했습니다.
단일 스레드란 한 번에 하나의 작업만 실행할 수 있다는 의미입니다.
현재 실행 중인 작업이 종료되어야 다음 작업이 시작됩니다.
이러한 구조는 프로그램의 동작을 예측하기 쉽도록 만들지만
동시에 오래 걸리는 작업이 발생하면 전체 프로그램이 멈추는 문제가 있습니다.
예를 들어 서버 응답을 기다리는 동안 모든 기능이 정지된다면
사용자는 매우 불편한 경험을 하게 됩니다.
이를 해결하기 위해 브라우저는 JavaScript 엔진 외부에 다양한 비동기 처리 시스템을 제공하며,
Event Loop가 이를 조율하는 역할을 담당하게 됩니다.
Call Stack의 역할
Event Loop를 이해하기 위해서는 먼저 Call Stack을 알아야 합니다.
Call Stack은 현재 실행 중인 함수들을 저장하는 공간입니다.
함수가 호출되면 스택에 추가되고,
실행이 완료되면 제거됩니다.
JavaScript 엔진은 항상 Call Stack의 가장 위에 있는 작업부터 실행합니다.
만약 함수 내부에서 또 다른 함수를 호출하면
새로운 함수가 스택 위에 추가됩니다.
이러한 구조 때문에 함수 실행 순서가 명확하게 관리됩니다.
Call Stack이 비어 있어야만 새로운 작업을 처리할 수 있습니다.
따라서 긴 연산이 실행 중이라면 다른 작업들은 대기 상태가 됩니다.
Event Loop는 바로 이 Call Stack의 상태를 지속적으로 확인하면서 동작합니다.
Web API는 어떤 역할을 할까
브라우저에는 JavaScript 엔진 외부에서 제공하는 다양한 기능이 존재합니다.
이를 일반적으로 Web API라고 부릅니다.
대표적인 예로는 setTimeout,
DOM 이벤트,
Fetch API,
Ajax 요청,
Geolocation API 등이 있습니다.
JavaScript 코드가 이러한 기능을 호출하면
실제 작업은 Web API 영역에서 처리됩니다.
예를 들어 setTimeout을 실행하면
JavaScript 엔진은 타이머를 직접 관리하지 않습니다.
대신 브라우저의 타이머 시스템에 작업을 위임합니다.
그 결과 JavaScript는 다른 작업을 계속 수행할 수 있으며
브라우저는 지정된 시간이 지나면 해당 작업을 다시 실행 대기 상태로 전환합니다.
이러한 구조 덕분에 JavaScript는 단일 스레드임에도 비동기 처리가 가능합니다.
Task Queue와 Callback Queue
Web API에서 작업이 완료되면 실행해야 할 함수가 Queue에 저장됩니다.
이를 Task Queue 또는 Callback Queue라고 부릅니다.
Queue는 선입선출 구조를 사용합니다.
먼저 들어온 작업이 먼저 실행됩니다.
중요한 점은 Queue에 들어갔다고 해서 즉시 실행되는 것이 아니라는 점입니다.
Call Stack이 비어 있어야만 Queue의 작업이 실행될 수 있습니다.
만약 현재 긴 연산이 진행 중이라면
Queue에 저장된 작업들은 계속 기다려야 합니다.
따라서 setTimeout의 시간이 종료되었다고 해서 정확히 그 시점에 실행되는 것은 아닙니다.
Call Stack이 비워진 이후에야 실행될 수 있습니다.
이러한 특징은 실제 서비스 개발 과정에서 성능 문제를 분석할 때 매우 중요하게 작용합니다.
Event Loop가 실제로 수행하는 일
Event Loop의 역할은 생각보다 단순합니다.
지속적으로 Call Stack을 감시하면서
현재 실행 중인 작업이 모두 종료되었는지 확인합니다.
Call Stack이 비어 있다면
Task Queue에 대기 중인 작업을 가져와 실행합니다.
반대로 Call Stack이 비어 있지 않다면
Queue에 있는 작업은 계속 대기합니다.
Event Loop는 이러한 과정을 매우 빠르게 반복합니다.
사용자는 실제로 여러 작업이 동시에 실행되는 것처럼 느끼지만
내부적으로는 Event Loop가 작업 순서를 관리하며 하나씩 처리하는 구조입니다.
결국 Event Loop는 JavaScript 비동기 처리 시스템의 핵심 제어 장치라고 볼 수 있습니다.
Microtask Queue가 더 우선순위가 높은 이유
현대 JavaScript에서는 일반 Task Queue 외에도 Microtask Queue가 존재합니다.
대표적으로 Promise의 then,
catch,
finally 등이 Microtask Queue를 사용합니다.
중요한 특징은 Microtask Queue가 일반 Task Queue보다 높은 우선순위를 가진다는 점입니다.
Call Stack이 비워지면 Event Loop는 먼저 Microtask Queue를 모두 처리합니다.
그 이후에 일반 Task Queue를 처리합니다.
이 때문에 Promise가 setTimeout보다 먼저 실행되는 현상이 발생합니다.
실제 면접에서도 자주 등장하는 질문 중 하나가 바로 이러한 실행 순서 문제입니다.
Microtask Queue의 존재를 이해하면 JavaScript 실행 결과를 더욱 정확하게 예측할 수 있습니다.
실무에서 Event Loop를 알아야 하는 이유
Event Loop는 단순한 이론 개념이 아닙니다.
실제 서비스 성능과 직접적으로 연결됩니다.
대용량 데이터 처리,
실시간 채팅,
대규모 API 통신,
복잡한 사용자 인터페이스 구현 과정에서 Event Loop의 이해는 필수적입니다.
특히 렌더링 지연 현상이나 UI 멈춤 현상을 분석할 때
Call Stack과 Queue의 상태를 파악할 수 있어야 합니다.
React, Vue, Angular 같은 프레임워크 또한 내부적으로 Event Loop 구조를 기반으로 동작합니다.
비동기 코드가 많아질수록 Event Loop에 대한 이해가 개발 생산성과 디버깅 능력에 큰 영향을 미칩니다.
따라서 JavaScript를 깊이 있게 학습하고 싶다면
Event Loop는 반드시 이해해야 하는 핵심 개념 중 하나라고 할 수 있습니다.
Summary
Event Loop
• JavaScript 비동기 처리의 핵심 메커니즘.
• Call Stack 상태를 지속적으로 감시함.
• Stack이 비어 있으면 Queue의 작업을 실행함.
• 단일 스레드 환경에서 비동기 처리를 가능하게 만듦.
Call Stack & Queue
• Call Stack은 현재 실행 중인 작업을 저장함.
• Web API는 비동기 작업을 대신 처리함.
• Task Queue와 Microtask Queue가 존재함.
• Promise는 일반 Task보다 우선 실행되는 Microtask를 사용함.