개발 언어/자바스크립트

Node JS 작동 원리 (1) - V8 엔진과 Libuv 라이브러리

Chaein.P 2022. 3. 20. 14:12

이 포스트는 Udemy의 NodeJs: Advanced Concepts 강의를 보고 정리한 study log입니다.

Node JS 의 구성 요소

우리가 작성한 자바스크립트 코드는 Node JS를 통해 실행된다. 보통은 node index.js 와 같은 명령어를 사용한다.

NodeJS는 실행 과정에서 크게 두개의 의존성 집합을 갖게 된다. 바로 V8에 대한 의존성과 libuv라이브러리에 대한 의존성이다.

 

V8 Engine

V8 Engine은 구글에 의해 개발된 오픈소스 JavaScript 엔진이다. V8의 목적은 JS 코드를 브라우저가 아닌 환경에서 실행하는 데에 있다.

 

Libuv 라이브러리

Libuv 라이브러리는 C++ 오픈 소스 프로젝트로 node가 OS의 파일 시스템이나 네트워크, 동시성 제어 등에 접근할 수 있도록 한다.

 

그렇다면 Node JS는 무슨 역할을 수행할까? 왜 우리는 JavaScript 코드를 실행하는 데 V8 Engine이나 Libuv라이브러리를 직접 사용하지 않고 Node JS를 사용하는 것일까? 이유는 V8 과 libuv 라이브러리는 JS 코드로 직접 사용할 수 없기 때문이다. V8은 70%가 C++코드로 되어있으며 libuv라이브러리는 100% C++로 이루어져있다.

 

Node JS는 JS 코드로만 이루어진 애플리케이션을 컴퓨터가 해석하고 실행할 수 있는 C++ 언어로 변환해주는 interface를 제공한다. 예를 들어 Node JS의 내장 모듈인 http, fs, crpto, path 등은 모두 libuv라이브러리의 기능을 참조한다. 이 모듈들을 통해 우리는 C++코드로 libuv 라이브러리 기능에 접근하지 않고 JS로 해당 기능을 사용할 수 있다.

코드로 살펴보는 Node JS 작동 원리

위와 같은 구성이 실제로 코드 상에서 어떻게 구현되어 있는지 살펴보고자 한다. Node JS의 내장 모듈을 사용해 해당 모듈이 어떻게 V8엔진과 Libuv라이브러리를 참조하는지 직접 확인해볼 것이다. 사용할 함수는 crpto 모듈의 pdkdf2라는 해시 함수다. Node JS 코드는 오픈소스이기 때문에 여기서 확인할 수 있다.

 

주로 살펴볼 폴더는 lib와 src다. lib는 모든 JavaScript 함수와 모듈들의 정의가 들어있다. src 폴더에는 lib에 정의 되어있는 함수들이 C++로 구현되어있다. src 폴더의 내용들이 실제 Node JS가 실행될 때 V8엔진과 libuv기능에 접근하기 위해 사용되는 코드라고 볼 수 있다.

 

먼저, lib > internal > crypto > pdkdf2.js 파일을 확인해보자. import, 함수 정의, export 로 구성된 익숙한 자바스크립트 코드를 볼 수 있다. 가장 상단에 정의된 pbkdf2의 내부 구조를 살펴보면 PBKDF2라는 함수를 호출하고 있다. 이 함수는 외부에서 import 되어있는데 그 형태가 익숙하진 않다. 이 코드가 바로 C++로 구현된 함수를 불러오는 코드이다.

 

const { PBKDF2 } = process.binding('crypto');

 

process.binding은 JavaScript 코드와 C++ 코드를 연결해주는 역할을 한다. 그럼 src > noce_crypto.cc 파일을 확인해보자. 5000줄이 넘는 방대한 C++ 코드를 볼 수 있는데 이 코드들이 실질적으로 Node JS의 crypto 모듈을 구현한 구현체이다. 파일의 하단 부를 살펴보면 다음과 같은 export 형식을 볼 수 있다.

 

env -> SetMethod(target, "PBKDF2", PBKDF2);

 

이 부분이 pdkdf2.js 파일에서 process.binding()에 의해 import 되고 있는 부분이다. 이를 통해 우리가 작성한 JS 코드는 궁극적으로 C++ 코드에 의존해 실행되고 있음을 알 수 있다. 그럼 V8 엔진과 libuv 라이브러리는 여기서 어떻게 사용되고 있을까? 파일의 상단부를 살펴보면 다음과 같은 코드를 찾을 수 있다.

 

using v8::Array;
using v8::Boolean;
using v8::Context;

 

위와 같은 코드들은 C++코드로 재정의된 JavaScript 개념들을 import 하는 구문이다. 예를 들어 using v8::Array 는 C++ 로 구현된 JavaScript의 Array를 import해 사용하겠다는 뜻이다. 이처럼 V8엔진은 JavaScript 코드들을 C++ 코드로 번역해주는 역할을 수행하고 있음을 알 수 있다.

 

그렇다면 libuv라이브러리는 어떻게 사용되고 있을까? 해당 파일에서 uv_ 접두사로 시작되는 코드들을 발견할 수 있다. 이 코드들이 libuv 라이브러리를 사용하는 것이데 그 쓰임을 살펴보면 C++ 로 동시성과 프로세싱 구조를 다루는데 사용되는 것을 알 수있다.

 

정리하자면 우리가 사용하는 JavaScrpt 모듈 및 내장 함수들은 내부적으로 C++ 로 구현되어 있으며, C++ side에선 V8엔진과 libuv라이브러리 간의 *상호 운용성을 토대로 작동한다.

 

*상호 운용성 : 하나의 시스템이 동일 또는 이기종의 다른 시스템과 아무런 제약이 없이 서로 호환되어 사용할 수 있는 성질