본문 바로가기
  • 프론트엔드 개발자 세오세오 | Frontend dev Seo
Learn to Code

[JS] 이벤트룹 (Event loop), 힙(Heap), 콜 스텍(Call stack)

by CEOSEO 2021. 11. 18.
728x90
반응형

* 본 포스팅은 이벤트 룹이 무엇인지 공부하고 정리한 내용을 재작성한 것입니다.

 

자바스크립트 런타임 환경의 동작 원리를 이해할 때 빼놓을 수 없는 주제가 바로 이벤트룹(event loop)이다. 이 이벤트룹의 존재로 인하여 자바스크립트 엔진이 싱글 스레드(single-thread)임에도 불구하고 브라우저 또는 Node.js와 같은 환경에서 마치 멀티 스레드로 동작하는 것과 같은 효과를 낼 수 있게 되는 것이다. 

 

 

 

이벤트룹(Event loop)이란?

이벤트룹(Event loop)은 자바스크립트 런타임 환경에 포함되어 있는 기능 중 하나로, (1) 자바스크립트 엔진 내 콜 스택(Call stack)이 비어있고(2) 런타임 환경 내 이벤트큐(Event queue(task queue 또는 message queue라고 부르기도 한다))에 실행 대기 중인 비동기 함수가 있을 경우 그 대기중이던 함수를 콜스택에 푸시하는 기능을 수행한다. 

 

즉 ,이벤트룹이 있기 때문에 단순히 콜 스텍에 들어온 실행 컨텍스트를 최상단부터 순서대로 실행하기만하는 자바스크립트 엔진임에도 불구하고 자바스크립트에 동시성이 제공될 수 있게 되는 것이다.

 

📝 이벤트큐(Event queue) 또는 태스크큐(Task queue)
- 이벤트룹과 마찬가지로 자바스크립트 런타임 환경에 포함된 기능으로, 비동기함수의 콜백함수 또는 이벤트 햄들러가 일시적으로 보관되는 영역이다.
- 이름이 알려주듯 큐(queue) 자료구조 형태를 가지고 있으며, 그렇기 때문에 가장 먼저 들어온 작업이 가장 먼저 처리되는 특징을 가진다(First in First out).

 

 

 

 

 

자바스크립트 엔진: 힙(Heap)과 콜 스텍(Call stack)

만약 이벤트룹이 없다면 - 이벤트룹과 같은 자바스크립트 엔진 외부에 속하는 이벤트룹과 같은 +@ 기능들이 없다면 - 어떨까? 이에 대한 답은 자바스크립트 엔진을 살펴보면 알 수 있다.

 

위 그림에 나온 것과 같이, 자바스크립트 엔진은 크게 두 가지 구성요소로 이루어져있다. 힙(Heap)이라는 부분과 콜 스택(Call stack)이라는 부분이 바로 그 두 가지 요소이다. 

 

콜 스택은 실행 컨텍스트가 추가 또는 제거를 통해 코드의 실행 순서를 관리하는 스택 자료구조를 뜻한다. 스택 자료구조이기 때문에, 가장 나중에 추가된 실행 컨텍스트 - 즉, 최상단에 있는 실행 컨텍스트 - 가 현재 실행되고 있는 실행 컨텍스트가 된다 (Last in, first out). 소스코드의 실행이 모두 종료되면, 최상단에 있던 실행 컨텍스트는 콜 스택에서 pop되어 사라진다. 콜 스택은 실행 컨텍스트의 추가 및 제거를 통해 코드의 실행 순서를 관리하기 때문에, 실행 컨텍스트 스택(execution context stack)이라고도 불린다.

 

힙은 콜 스택의 최상단에 있는 실행 컨텍스트(execution context)가 실행되면서 참조되는 객체들이 저장되어 있는 메모리 공간이다. 객체의 크기는 런타임에 결정(=실행된 이후 동적으로)되기 때문에 객체가 저장되는 메모리 저장소인 힙은 구조화되어있지 않다.

 

 

📝 실행 컨텍스트(Execution context)
- 자바스크립트 소스코드는 (1) 평가(2) 실행이라는 두 가지 단계를 거쳐 처리된다. 첫번째 단계인 평가 과정에서 소스코드의 실행 컨텍스트가 생성된다.
- 실행 컨텍스트는 식별자의 등록 및 관리와 코드 실행 순서를 관리하는 메커니즘이다. 
- 실행 컨텍스트는 렉시컬 환경(lexical environment)를 통해 식별자와 스코프를 관리하며, 콜 스택(Call stack)으로 코드 실행 순서를 관리한다.

 

 

콜 스택 하나만을 가진 자바스크립트 엔진은 싱글 스레드이다. 그렇기 때문에, 이벤트룹과 같이 런타임 환경에 포함된 기타 기능 없이 자바스크립트 엔진 혼자서는 콜 스택을 통해 요청된 작업을 순차적으로 실행하는 굉장히 단순한 역할만을 담당한다.

 

그렇기 때문에, 브라우저가 마치 여러가지 일을 동시에 처리하는 것과 같은 효과를 주기 위해선 이벤트룹(event loop)과 이벤트큐(event queue 또는 task queue)의 존재가 필수적이다. WebAPI나 HTTP 요청와 같은 비동기 처리에서 소스 코드를 평가하고 실행하는 임무를 제외한 다른 모든 일들은 자바스크립트 엔진이 아닌 런타임 환경인 브라우저/Node.js가 담당하기 때문이다. 그 환경에 포함된 것이 이벤트룹과 이벤트큐이다.

 

 

 

⏰ 브라우저의 타이머와 최소 지연 시간 4ms 

setTimeout(콜백함수시간)

 

setTimeout은 '시간ms 이후 콜백함수를 실행해야되!!'를 뜻하는 비동기 함수이다. setTimeout의 두번째 인자로 전달하는 시간은 "##밀리초 후에 해당 콜백함수를 이벤트큐에 푸시해라!!"라는 걸 의미한다.

 

그렇다면 만약, '0초 후 케이크를 먹는다'는 콜백함수를 실행하고 싶어서 아래와 같이 작성했을 때, 우리는 정말 0초 후에 케이크를 먹을 수 있을까??

 

 

function eatCake () {
  console.log('냠냠')
}

setTimeout(eatCake, 0)

 

정답은 아니오(NOPE)이다. 왜 아니오인지 알기 위해서 위 코드가 어떤 순서로 진행되는지 살펴보자.

 

1. 전역 코드가 평가된다.

  - 전역 실행 컨텍스트가 생성된다.

  - eatCake 함수가 전역 함수로 등록된다.

  - 콜 스택에 전역 실행 컨텍스트가 푸시된다.

 

2. 전역 코드가 실행된다.

 

3. setTimeout 함수가 호출된다.

  - setTimeout 함수의 함수 실행 컨텍스트가 생성된다.

  - 생성된 setTimeout 함수의 함수 실행 컨텍스트가 콜 스텍에 푸시된다.

 

4. setTimeout 함수가 실행된다.

  - ✨ 브라우저가 콜백 함수인 eatCake의 호출을 스케줄링한다. 

  - setTimeout의 실행이 종료되고, 콜 스택에서 팝되어 사라진다.

 

5. 브라우저, 타이머를 설정하고 타이머의 만료를 기다린다.

  - 타이머가 만료되면 - 즉, setTimeout의 두번째 인자로 전달한 시간이 지나면 -  콜백함수 eatCake가 이벤트큐에 푸시된다.

  => 그러나, 위 예시의 경우 0ms라고 시간을 전달했음에도 불구하고, 만약 지연 시간이 4ms 이하로 표기된 경우엔 최소 지연 시간 4ms로 디폴트로 지정되게 된다. 그렇기 때문에, eatCake는 0ms가 아닌 4ms 후에 이벤트큐에 푸시되어 대기 상태에 들어가게 된다.

 

- 4ms가 지나 타이머가 만료되면, 콜백 함수 eatCake가 이벤트큐에 푸시된다.

 

6. 전역 코드 실행이 종료된다.

  - 전역 실행 컨텍스트가 콜 스택에서 팝되어 사라진다.

 

7. 콜 스택이 비었고 && 이벤트큐에 대기 중인 함수 eatCake가 있기 때문에, 이벤트룹은 eatCake를 콜 스택에 푸시한다.

  - eatCake 함수의 실행 컨텍스트가 생성된다.

 

8. eatCake 함수가 평가되고 실행된다.

  - 컨솔에 "냠냠"이 찍힌다.

 

9. eatCake 함수의 실행이 종료된다.

  - eatCake 함수의 실행 컨텍스트가 콜 스택에서 팝되어 사라진다.

 

 

결과적으로, 그래서 얼마 후에 케이크를 먹을 수 있는 것일까?

케이크 시식은 0초의 딜레이를 주고 콜백함수 호출 스케줄링을 했음에도 불구하고, 최소 지연 시간인 4ms를 더해야한다는 점과 전역 실행 컨텍스트가 setTimeout을 호출하는 지점 이후의 코드들도 모두 실행이 종료되고 (위 예시엔 없지만) 콜 스택이 텅텅 빌때까지의 시간을 더한 뒤에야 이루어질 수 있게 된다. (즉, 4ms + @)

 

 

 

 

 

 

 

 

필수 시청

https://www.youtube.com/watch?v=8aGhZQkoFbQ 

이벤트룹의 역할을 알기 위해 필수적으로 봐야하는 발표

 

728x90
반응형

댓글