본문 바로가기

JavaScript

JavaScript 런타임과 V8 엔진, 비동기 처리 원리

 

JavaScript 런타임이란?

 

 JavaScript 런타임이란 JavaScript가 구동되는 환경을 말한다. JavaScript 런타임의 종류로는 웹 브라우저(크롬, 파이어폭스 등) 프로그램과 Node.js가 있다. 이러한 프로그램들에서 JavaScript가 구동되기 때문에 JavaScript 런타임이라고 한다. JavaScript 는 Node.js 의 등장 전에는 웹 브라우저에서만 실행될 수 있는 클라이언트 언어였지만, Node.js 같은 브라우저 외부의 런타임 환경이 등장하면서 웹 브라우저 바깥에서도 실행할 수 있는 언어가 되었다. 

 

 

 

V8 엔진

 

 JavaScript 코드를 컴파일하고 실행하기 위해서는 JavaScript 엔진이 필요하다. V8 엔진은 Google에서 개발한 오픈소스 JavaScript 엔진으로, Node.js와 Google Chrome에서 사용된다. V8 엔진은 JavaScript 코드를 컴파일하고 실행하는 역할을 수행한다.

 


V8 엔진은 다음과 같은 기술을 사용한다.

 

  • JIT 컴파일러: V8 엔진은 Just-In-Time(JIT) 컴파일러를 사용하여 JavaScript 코드를 빠르게 컴파일하고 실행한다. JIT(Just-In-Time) 컴파일러는 코드를 컴파일하는 방법 중 하나로, 프로그램을 실행하는 동안 필요한 코드를 즉석에서 컴파일하여 실행한다. 이를 통해 실행 속도를 향상시킬 수 있다.

 

  • 메모리 관리: V8 엔진은 JavaScript의 가비지 컬렉션(Garbage Collection) 기능을 지원하여 메모리를 효율적으로 관리한다. V8 엔진은 메모리 누수를 방지하고 자동으로 메모리를 해제한다.

 

  • 최적화: V8 엔진은 코드 실행 중에 최적화를 수행하여 더욱 빠른 실행 속도를 보장한다. 예를 들어, 반복문 내에서 동일한 코드 블록이 반복될 경우, V8 엔진은 이를 인식하고 최적화를 수행하여 실행 속도를 향상시킨다.

JavaScript V8 엔진 소스 내부에는 하나의 힙(heap)과 하나의 호출 스택(call stack)이 있다.

 

 

 

 

 

 

 

비동기 처리

 

동기와 비동기

 

● 동기(Synchronous) 작업은 직렬적으로 실행되며, 이전 작업이 끝나야 다음 작업을 실행할 수 있다. 즉, 현재 작업이 완료되기 전까지 다음 작업은 대기해야 한다. 이로 인해 동기 작업을 실행하는 동안 UI가 멈추는 경우가 발생할 수 있다.

● 비동기(Asynchronous) 작업은 병렬적으로 실행된다. 이전 작업이 완료되지 않았더라도 다음 작업을 실행할 수 있다. 즉, 작업의 완료를 기다리지 않고 다른 작업을 수행할 수 있다. 이 때문에 UI가 멈추지 않고, 작업이 느리게 실행되더라도 다른 작업을 수행할 수 있다.

 

 

 JavaScript는 Single Thread 언어이다. JavaScript의 Main Thread인 이벤트 루프가 Single Thread이기 때문이다. 즉, 한 번에 하나의 작업만 처리할 수 있다. 한 번에 하나의 작업만 처리할 수 있기 때문에 동기 방식이다.  

 

 

블로킹 상태

 

 Single Thread로 AJAX 요청(웹 브라우저에서 비동기적으로 서버와 데이터를 주고받는 기술, AJAX를 사용하면 페이지 전체를 다시 로드하지 않고도 서버와 데이터를 주고받을 수 있다)을 동기적으로 보낼 시 요청을 보낸 후 서버로부터 응답이 올 때까지 코드의 실행이 일시 중지된다. 이렇게 되면, 코드가 종료될 때까지 사용자는 웹 페이지에서 다른 작업을 할 수 없고, 페이지가 멈춘 것처럼 보일 수 있다. 이 상태를 Call Stack이 멈춘 상태임을 나타내는 블로킹 상태라고 한다.

 

 동기적 처리의 문제점을 해결하기 위해 웹 브라우저에서 제공하는 Web API, Node.js에서 자체적으로 지원하는 API 같은 외부 API를 통해 AJAX 요청을 비동기적으로 실행한다. JavaScript 자체는 비동기적으로 요청을 처리할 수 없지만, JavaScript 런타임 안에서 지원하는 API로 비동기 요청을 처리할 수 있게 하는 것이다.

 

 

 

비동기 처리 방법

 

 JavaScript엔진(V8)은 메모리 힙과 콜 스택을 가지고 있다.

메모리 힙은 변수와 객체의 메모리 할당에 사용되는 비정형 메모리이다.

콜 스택은 코드를 읽고 함수가 실행되는 순서를 기억하고 있다. 함수를 실행하려면 스택의 가장 위에 해당 함수를 넣게 되고 함수에서 리턴이 일어나면 pop을 한다.

 

 

웹 브라우저의 WebAPI를 통한 비동기 처리

 

 Javscript를 사용하면서 우리가 많이 사용하는 API 들은 사실 JavaScript에서 지원하는 것이 아닌 웹 브라우저에서 제공하는 API로 DOM , AJAX, Timeout 등이 있다. 콜 스택에서 실행된 비동기 함수는 Web API에서 처리를 하게 되고 그동안에 Call Stack은 나머지 동기 함수들을 처리하게 된다. Web API는 비동기 함수들을 처리하며 작업이 완료된 비동기 함수들을 Callback Queue로 넘겨주게 된다.

 

 

Callback Queue

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

 

 

Event Loop

 Event Loop는 Call Stack과 Callback Queue를 상태를 계속 감시하며 Call Stack에 함수들이 존재하지 않는다면 Callback Queue에 있는 비동기 함수들을 Call Stack에 밀어 넣게 된다. 그 후 Call Stack에서 비동기 함수를 실행시키게 된다.

 

 

 

 

Node.js에서의 비동기 처리

 

 Node.js의 내부 구조는 Node.js의 내장 라이브러리(llhttp, c-ares, OpenSSL, zlib), V8 엔진, libuv로 나누어볼 수 있다.

 

 Node.js에서 JavaScript에게 비동기 방식을 지원하기 위한 핵심 요소는 libuv이다. libuv는 다양한 I/O 및 시스템 작업을 지원하는 이벤트 기반의 비동기 I/O 라이브러리이다. I/O는 libuv가 지원하는 작업(http, Database와 통신, third party API, 파일 I/O ) 등 과의 상호작용( Input / Output )이다. 이벤트 루프와 비동기 작업을 처리하는 스레드 풀, 네트워크 처리, 파일 시스템 작업 등을 지원한다.

 

 libuv는 다음과 같은 과정을 통해 JavaScript에게 비동기 방식을 제공한다.

 

 ● 이벤트 루프(Event Loop): Node.js의 이벤트 루프는 libuv를 기반으로 동작한다. 이벤트 루프는 I/O 작업을 비동기적으로 처리하고, 완료되면 콜백 함수를 실행한다. 이를 통해 비동기적으로 코드를 실행할 수 있다. 이벤트 루프에 대한 자세한 설명을 추후 포스팅에서 다루도록 하겠다. 

 ● I/O 작업 처리: libuv는 I/O 작업을 처리하기 위해 스레드 풀을 사용한다. 이를 통해 I/O 작업을 비동기적으로 처리하고, 결과를 이벤트 루프로 반환한다. 

 ● 네트워크 처리: libuv는 네트워크 처리를 위한 API를 제공한다. 이를 통해 네트워크 작업을 비동기적으로 처리하고, 이벤트 루프를 통해 결과를 반환한다. 

 ● 파일 시스템 작업 처리: libuv는 파일 시스템 작업을 처리하기 위한 API를 제공한다. 이를 통해 파일 시스템 작업을 비동기적으로 처리하고, 이벤트 루프를 통해 결과를 반환한다. 

 

 

 

 

 

 

 

 

 JavaScript에서 비동기를 처리하는 방법(setTimeout, Promise 객체, async/await 등은 다음 포스팅에서 다룰 예정이다.

'JavaScript' 카테고리의 다른 글

(JavaScript)localStorage, JSON  (0) 2023.04.09
(JavaScript)문서 객체 다루기  (0) 2023.04.09
(JavaScript)forEach, map, filter  (0) 2023.04.08
(JavaScript)개념 정리  (0) 2023.04.01