본문 바로가기
Node.js

[Javascript] 이벤트 루프

by yonikim 2024. 6. 25.
728x90

 

Javascript 는 싱글 스레드 언어라고 들어본 적이 있을 것이다. '싱글' 스레드는 그 이름답게 한번에 하나의 작업만 수행할 수 있다.

하지만 Javascript 를 사용해보면 멀티 스레드처럼 동시에 여러 작업을 수행할 수 있다는 것을 알 수 있다. 

 

그렇다면 Javascript 는 정말 싱글 스레드 언어가 맞을까?

 

결론부터 이야기하자면 맞다. Javascript 의 메인스레드인 이벤트 루프가 싱글 스레드이기 때문이다. 

하지만 이벤트 루프만 독립적으로 실행되는 것이 아닌, 웹 브라우저나 Node.js 같은 멀티 스레드 환경에서 실행되고, 이를 적절하게 사용함으로써 멀티 스레드처럼 사용이 가능한 것이다. 

 

파일 다운, 네트워크 요청, 타이머, 애니메이션 같이 오래 걸리고 반복적인 작업들은 Javascript 엔진이 아닌 브라우저 내부의 멀티 스레드인 Web APIs 에서 비동기 + 논블로킹으로 처리된다. 

즉, 비동기로 동작하는 핵심요소는 Javascript 언어가 아니라 브라우저라는 소프트웨어가 가지고 있다고 보면 된다.

Node.js 에서는 libuv 내장 라이브러리가 처리한다.

 


Javascript 엔진 

Javascript 엔진의 대표적인 예는 Google 의 V8 엔진이 있다. V8 엔진은 Chrome 과 Node.js 에서 사용되며, 이외에도 각 브라우저 별로 여러가지 엔진들이 존재한다.

 

 

Javascript 엔진은 크게 두가지 요소로 이루어져 있다. 

  • Memory Heap: 메모리 할당이 일어나는 장소
  • Call Stack: 코드가 실행될 경우 하나씩 stack 의 형태로 쌓이는 장소

먼저 Memory Heap 에 있는 사용자가 작성한 코드들은 Call Stack 에서 스택 방식으로 쌓이며 코드를 실행하게 되는데, 이때 동기 함수들은 그대로 실행하게 되고 비동기 함수들은 Web API 로 처리하게 되며 일을 분배한다. 

 

Web API 는 또 뭐야?

Javascript 를 사용해본 사람이라면 `setTimeout`, `addEventListener` 함수를 한번쯤은 사용해봤을 것이다. 

Javascript 를 사용하면서 우리가 많이 사용했던 API 들은, 사실 Javascript 에서 지원하는 것이 아닌 웹 브라우저에서 제공하는 API 였던 것이다.

 

 

 

 

다시 정리하자면,

Call Stack 에서 실행된 비동기 함수는 Web API 에서 처리를 하게 되고, 그동안 Call Stack 은 나머지 동기 함수를 처리한다. 

Web API 는 비동기 함수를 처리하며 작업이 완료된 비동기 함수들을 Callback Queue 에게 넘겨준다.

  • Callback Queue: 비동기 함수들을 보관하는 장소로, Event Loop 에서 비동기 함수를 꺼내 실행하기 전까지는 Queue 방식으로 보관하게 된다.

 

Web APIs  의 종류

Web APIs 는 타이머, 네트워크 요청, 파일 입출력, 이벤트 처리 등 브라우저에서 제공하는 다양한 API 를 포괄하는 총칭이다.

Web API 는 브라우저(Chrome)에서 멀티 스레드로 구현되어 있다. 그래서 브라우저는 비동기 작업에 대해 메인 스레드를 차단하지 않고 다른 스레드를 사용하여 동시에 처리할 수 있는 것이다.

  • DOM: HTML 문서의 구조와 내용을 표현하고 조작할 수 있는 객체
  • XMLHttpRequest: 서버와 비동기적으로 데이터를 교환할 수 있는 객체. AJAX 기술의 핵심
  • Timer API: 일정한 시간 간격으로 함수를 실행하거나 지연시키는 메소드들을 제공
  • Console API: 개발자 도구에서 콘솔 기능을 제공
  • Canvas API: <canvas> 요소를 통해 그래픽을 그리거나 애니메이션을 만들 수 있는 메소드들을 제공
  • Geolocation API: 웹 브라우저에서 사용자의 현재 위치 정보를 얻을 수 있는 메소드들을 제공

이때 오해하지 말아야할 것이 모든 Web API 들이 비동기로 동작되는 것이 아니다. 예를 들어 DOM API 나 Console API 는 동기적으로 처리되고, XMLHttpRequest 나 Timer API 는 비동기적으로 처리된다.

 

 

아니, 그래서 이벤트 루프가 뭔데? 

 

싱글 스레드인 자바스크립트의 작업을 멀티 스레드로 돌려 작업을 동시에 처리시키게 하던가, 또는 여러 작업 중 어떤 작업을 우선으로 동작시킬 것인지 결정하는 세심한 컨트롤을 하기 위해 존재하는 것이 바로 이벤트 루프(Event Loop) 이다.

 

이벤트 루프는 브라우저 내부의 Call Stack, Callback Queue, Web APIs 등의 요소들을 모니터링하면서 비동기적으로 실행되는 작업들을 관리하고, 이를 순서대로 처리하여 프로그램의 실행흐름을 제어하는 녀석이다.

 

이벤트 루프의 동작 과정을 간단히 살펴보자면, 자바스크립트의 `setTimeout` 이나 `fetch` 와 같은 비동기 코드를 브라우저 WebAPIs 에게 맡기고, 백그라운드 작업이 끝난 결과를 콜백 함수 형태로 큐(Callback Queue) 에 넣고 처리 준비가 되면 호출 스택(Call Stack) 에 넣어 마무리 작업을 진행한다. 

 


 

Node.js 의 내부 구성도

Node.js 환경에서도 브라우저와 거의 비슷한 구조를 볼 수 있는데, 차이점이 있다면 내장된 libuv 라이브러리를 사용하여 비동기 I/O 를 지원한다는 점이다.

Node.js 에서는 Web API 가 아닌 Node API 를 이용하여 파일 시스템 액세스, 네트워크 액세스, 암호화, 압축 및 해제 등과 같은 다양한 작업을 처리한다. 예를 들어 HTTP 요청을 수행하려면 http 모듈을 사용한다.

단, Node.js 에서도 일부 Web API 를 사용할 수 있는데, `setTimeout`, `setInterval` 등이 그렇다. 

  • V8: Node.js 에서 사용하는 자바스크립트 엔진으로 코드를 컴파일하고 실행한다.
  • Bindings (Node API): Node.js 시스템과 V8 엔진 간의 상호작용을 가능하게 하는 C++ 라이브러리
  • Libuv 라이브러리: 비동기 I/O 작업을 처리하기 위한 C 라이브러리
  • Event Queue: 비동기 I/O 작업 결과를 저장하고 처리하기 위한 자료구조 (웹 브라우저의 Task Queue 와 비슷)
  • Event Loop : Event Queue 에 저장된 I/O 작업 결과를 처리하고, 다음 작업을 수행하도록 하는 관리자
  • Worder Threads: CPU 의 집약적인 작업을 처리하기 위해 Node.js 10 버전부터 추가된 멀티 스레드. 메인 스레드와 독립적인 V8 엔진 인스턴스를 가진다.

웹 브라우저와 Node.js 의 Web API 차이

웹 브라우저의 Web APIs 는 비동기 작업이 끝나면 스스로 Callback Queue 에 적재하지만, Node.js API 들은 이벤트 루프가 직접 옮겨준다.

  • 웹 브라우저: Timer API 가 스스로 Task Queue 에 콜백 함수를 추가한다.
  • Node.js: Timer API 가 타이머 완료 이벤트를 발생시키고, 이벤트 루프가 이를 감지하여 Task Queue 에 콜백 함수를 추가한다.

 

 

References

https://inpa.tistory.com/entry/🔄-자바스크립트-이벤트-루프-구조-동작-원리

https://medium.com/sessionstack-blog/how-does-javascript-actually-work-part-1-b0bacc073cf

728x90