이벤트루프

김정훈·2023년 5월 10일

JavaScript

목록 보기
10/10

이벤트 루프는 자바스크립트 엔진이 비동기 처리를 위해 사용하는 핵심적인 개념 중 하나이다. 자바스크립트는 기본적으로 싱글 스레드로 동작하는 언어로, 한 번에 하나의 작업만 처리할 수 있다. 그러나 브라우저 환경에서는 비동기적으로 처리돼야 하는 경우가 많기 때문에, 이벤트 루프가 이를 관리하고 실행한다.

이벤트 루프의 역할

Call Stack의 동작관리

Call Stack은 자바스크립트 엔진이 함수 호출을 관리하는 데 사용되는 메커니즘이다. 함수가 호출되면 해당 함수의 실행 컨텍스트가 Call Stack에 Push 되고, 함수 실행이 완료되면 pop된다. 이 과정에서 이벤트 루프는 Call Stack이 비어있는지 주기적으로 확인하고, 비어 있으면 다음 작업을 실행한다.

Task Queue의 동작 관리

Task Queue는 비동기 작업이 완료되면 해당 작업을 큐에 push하는 대기열이다. 이벤트 루프는 Call Stack이 비어있을 때에만 Task Queue에서 작업을 꺼내와 Call Stack에 push하여 실행한다.

나는 이전에 프로젝트를 진행하면서 fatch then 구문 안에서 비동기 함수를 실행시킨 경험이 있다. 내가 기대한 것은 비동기 함수를 먼저 실행하고 그 뒤 코드들을 실행하는 것이었는데, 잘 실행되지 않았다.
추측컨데 then의 실행 컨텍스트가 아직 Call Stack에 남아있는 상태이기 때문에 내가 실행하고자 하는 비동기 함수는 태스크 큐에서 기다리고 있는 상태였을 것이다.

이벤트 처리

브라우저에서는 마우스 클릭, 키보드 입력 등 다양한 이벤트가 발생한다. 이벤트 루프는 이러한 이벤트가 발생하면 이를 Task Queue에 추가하고, Call Stack이 비어 있을 때 해당 이벤트를 처리한다.

자바스크립트 엔진의 실행 과정

자바스크립트 엔진은 코드 실행을 위해 다음과 같은 과정을 거친다.

1. 소스코드 평가단계(Parsing)

파싱 단계에서 소스코드가 실행 가능한 문장인지 검사한다. 코드에 문법적 오류가 존재하는지 판단하고 변수와 함수의 선언을 찾아서 실행 컨텍스트에 등록하게 된다.

2. 소스코드 실행단계

자바스크립트 코드를 실행시킬 때는 다음과 같은 과정을 거친다.

  • 실행 컨텍스트 생성
  • Call Stack에 새로운 실행 컨텍스트를 push
    • 실행 컨텍스트에서 변수와 함수에 접근할 수 있음
    • 실행 컨텍스트에서 this, argument 등의 특별한 값들이 설정됨
  • 연산자를 실행하거나 함수를 호출하여 결과값을 만듦
  • 다음 문장으로 이동하여 위 과정을 반복
  • 실행이 끝나면 Call Stack에서 실행 컨텍스트를 pop

3. 메모리 관리 (Garbage Collection)

  • 더 이상 사용하지 않는 메모리를 자동으로 해제

자바스크립트 엔진은 소스코드를 평가하고 실행하는 과정을 반복한다. 이 과정에서 Call Stack이 중요한 역할을 수행한다. Call Stack은 현재 실행 중인 함수의 실행 컨텍스트를 저장하고, 함수 실행이 끝난 후에는 이전 실행 컨텍스트로 돌아가기 위해 사용된다. 이를 통해 자바스크립트 엔진은 실행 컨텍스트를 관리하며, 이를 통해 자바스크립트의 스코프 체인, 클로저, this 등의 동작 원리를 구현한다.

Call Stack & Task Queue

지금까지 여러번 언급되었던 Call Stack과 Task Queue에 대해 알아보자.

Call Stack은 현재 실행 중인 함수의 실행 컨텍스트를 저장하는 자료구조로, 함수가 호출될 때마다 스택에 정보가 추가되고, 함수의 실행이 끝나면 스택에서 제거된다. 스택은 후입선출(LIFO)의 구조를 가지므로, 가장 마지막에 추가된 함수가 가장 먼저 실행되며, 이를 통해 함수 호출의 순서와 흐름을 제어한다.

Task Queue는 Call Stack과 달리, 비동기 처리를 위한 자료구조이다. Task Qeueu에는 비동기적으로 실행될 콜백 함수나 이벤트 핸들러가 저장되어 있으며, 이벤트 루프가 Call Stack이 비어있을 때 Task Qeueu에서 대기 중인 함수를 꺼내와 push한 뒤 실행한다.

이 두 자료구조의 가장 큰 차이점은 동기적으로 실행되는 함수와 비동기적으로 실행되는 함수의 처리 방식이다. Call Stack은 동기적으로 실행되는 함수들을 처리하면서 스택에 쌓이고, 마지막에 추가된 함수가 먼저 실행된다. 반변에, 비동기 함수나 이벤트 핸들러 같은 비동기적으로 실행되는 함수들은 Task Queue에 콜백 함수나 이벤트 핸들러로 대기되어 이벤트 루프에 의해 스택에 추가되어 실행된다.

Call Stack이 텅텅 비어있어야 Task Queue에서 비동기 함수를 꺼내 올 수 있다는 사실을 기억하자.

이벤트 루프의 동작 방식

글을 읽으면서 이벤트 루프가 대충 어떻게 동작하는지 알 수 있을 것이다.

비동기 함수는 어딘가에서 대기하고 있다가 실행을 시킬 때가 되면 Task Queue에서 자신의 차례를 기다린다. 이벤트 루프는 Call Stack을 관찰하면서 비어있을 때 Task Queue에 있는 콜백함수를 꺼내어 Call Stack에 push해준다.

예를 들어, 대표적으로 setTimeout, setInterval함수나 AJAX요청 등의 비동기 처리 함수들은 Task Queue에 자신이 가지고 있는 콜백 함수를 등록하고, 이벤트 루프에 의해 Call Stack이 비어 있을 때 콜백 함수의 실행 컨텍스트를 Call Stack에 push하여 실행한다.

그렇다면 이벤트 콜백 함수와 같은 특정 조건이 성립해야 실행되는 비동기 함수들은 자신의 차례를 어떻게 알 수 있을까?

일반적으로 비동기 함수를 처음 호출하면, 그 함수는 Call Stack에 push된다. 그러나 해당 함수 내부에서 비동기 작업을 수행하는 경우, 해당 작업을 처리하기 위해 자바스크립트 엔진은 백그라운드로 동작을 넘기게 된다.

백그라운드는 Web API를 비롯한 자바스크립트 엔진 밖에서 동작하는 비동기 처리를 위한 영역이다. setTimeout, setInterval함수나 AJAX요청은 백그라운드에서 실행된다. 비동기 처리 함수들은 실행이 완료되면 Task Queue에 콜백 함수를 넣게 된다.

이벤트 루프는 콜 스택이 비어있는 상태에서 Task Queue에 등록된 콜백 함수를 하나씩 가져와 Call Stack에 추가한다. 이벤트 루프가 Call Stack에 추가한 콜백 함수들은 순차적으로 실행되며, 새로운 콜백 함수들은 계속해서 Task Queue에서 대기하게 된다.

이런식으로 이벤트 루프가 지속적으로 돌아가며 Call Stack이 비어있을 때마다 Task Queue에서 콜백 함수를 꺼내와 실행한다.

0개의 댓글