NestJS / / 2024. 11. 2. 19:28

NodeJS 기술적 소개

 

오픈소스, 크로스 플랫폼, 자바스크립트 런타임

크로스 플랫폼 : 여러가지 OS에서 실행됨(맥, 윈도우, 리눅스)

javascript 엔진

v8, spiderMongkey, safari

v8 → 크롬에서 사용되는 엔진, 구글에서 제작, 구글지도가 너무 복잡해서 빠른속도를 유지하기위해 만들어진 엔진이라 속도가 빠름

컴파일드 언어 ⇒ 프로그램을 실행하기 전에 작성된 코드를 기계어로 한번에 모두 변환한 후 실행

인터프리티드 언어 ⇒ 프로그램을 실행하면서 동시에 코드를 한줄 한줄씩 변환해서 실행함

 

Compiled  Interpreted
프로그램을 실행하기 전에 기계어(Machine Code)로 전부 변환을 한다음 실행한다. 프로그램을 실행하는 도중에 각 코드를 줄별로 변환해서 실행한다.
기계어로 모두 변환이 된 상태에서 실행되기때문에 실행 과정은 빠르다 모든 코드가 변환이 된 상태로 실행되지 않기때문에 실행이 비교적 느리다.
작성한 코드가 변경될때마다 오랜시간 컴파일을 해야하는 문제가있다. 한번에 컴파일을 할 필요가 없기때문에 코드 변경이 있을때마다 매번 전체 컴파일을 할 필요가 없다.
타겟 플랫폼에서 직접 컴파일을 진행한다. 코드를 실행하는 또다른 프로그램(Interpreter)가 존재한다.

JIT( Just In Time Compilation)

JIT은 Compile방식과 Interpret 방식의 장점을 모아둔 형태이다.

V8 또한 JIT Compilation을 사용하고 있다.

Machine Code ⇒

  • Cpu가 바로 읽고 사용 할수 있는 바이너리로 구성된 코드이다.
  • 바이트 코드보다 컴퓨터에 더 가까운(Low Level)코드이다.
  • 실행이 매우 빠르다.
  • 컴파일이 느리다.
  • 플랫폼에 종속성이 있다. 하나의 운영체제에서 실행 할 수 있는 기계어는 다른 운영체제에서 실행 할수 없다.

 

Machine Code Byte Code
Cpu가 바로 읽고 사용 할수 있는 바이너리로 구성된 코드이다. 바이트 코드는 CPU가 바로 읽을 수 있는 코드가 아니다. 중간에 가상환경이나 또 다른 프로그램(Interpreter)이 실행을 중재한다.
바이트 코드보다 컴퓨터에 더 가까운(Low Level)코드이다. 머신코드보단 사람과 가까운(High Level)코드다.
실행이 매우 빠르다. 실행이 상대적으로 느리다.
플랫폼에 종속성이 있다. 하나의 운영체제에서 실행 할 수 있는 기계어는 다른 운영체제에서 실행 할수 없다. 컴파일이 빠르다
  플랫폼 종속성이 없다. Interpreter만 있으면 실행 가능함

Node.js는 **바이트코드(Bytecode)**를 사용하여 JavaScript 코드를 실행하는데, 이는 성능을 최적화하고 실행 속도를 높이기 위한 전략입니다. Node.js는 V8 엔진을 사용하며, V8 엔진은 Google에서 Chrome 브라우저를 위해 개발한 JavaScript 엔진입니다. 이 엔진의 핵심은 JavaScript 코드를 해석한 후, 바이트코드로 변환하여 실행하는 것입니다.

바이트코드를 사용하는 이유

  1. 성능 최적화:
    • V8 엔진은 JavaScript 코드를 바로 기계어로 변환하지 않고, 우선 바이트코드로 변환합니다.
    • 바이트코드는 JavaScript 코드의 중간 표현 형태로, 이를 통해 즉시 실행이 가능하고, JIT(Just-In-Time) 컴파일러를 통해 필요할 때마다 기계어로 변환합니다.
    • 이 방식은 JavaScript 코드를 바로 기계어로 변환하는 것보다 더 빠르게 실행할 수 있습니다. 모든 코드를 미리 컴파일하는 대신, 필요한 부분만 최적화하여 기계어로 변환하기 때문입니다.
  2. 메모리 효율성:
    • 바이트코드를 사용하면 메모리 사용을 효율적으로 관리할 수 있습니다.
    • 코드 전체를 기계어로 변환하는 대신, 필요한 부분만 컴파일하고 실행하여 메모리를 절약할 수 있습니다. 자주 실행되는 부분은 기계어로 최적화하여 저장하고, 그렇지 않은 부분은 바이트코드 상태로 남겨두어 메모리를 절약합니다.
  3. JIT 컴파일을 통한 실행 최적화:
    • V8 엔진의 JIT 컴파일러는 바이트코드를 실행하면서 자주 사용되는 코드를 분석하여, 더 빠른 실행을 위해 최적화된 기계어로 변환합니다.
    • 이 방식은 실행 중간에 성능 병목이 되는 부분을 최적화하여, 반복적으로 실행되는 함수나 루프의 속도를 크게 향상시킵니다.
  4. 호환성과 이식성:
    • 바이트코드는 플랫폼에 독립적입니다. 즉, 운영체제나 하드웨어에 맞춰 번역할 필요 없이 다양한 플랫폼에서 같은 바이트코드를 사용할 수 있습니다.
    • Node.js와 V8 엔진은 여러 플랫폼을 지원하는데, 바이트코드를 사용하면 같은 코드로 다양한 환경에서 실행할 수 있습니다.

바이트코드와 기계어의 차이

  • 바이트코드는 중간 단계의 코드로, 플랫폼에 독립적입니다. 이는 가상 머신(VM)에 의해 해석되거나 기계어로 컴파일되어 실행됩니다.
  • 기계어는 CPU가 직접 실행할 수 있는 코드로, 각 플랫폼(예: Windows, Linux)에 따라 다릅니다.

Node.js가 바이트코드를 사용하는 이유는 이러한 중간 단계를 통해 더 빠르고 효율적인 실행을 가능하게 하고, 다양한 환경에서 일관된 성능을 유지하기 위함입니다.

JIT

V8 엔진에서 JIT(Just-In-Time) 컴파일은 자바스크립트의 실행 속도를 높이기 위해 도입된 중요한 기술입니다. V8은 구글 크롬 브라우저와 Node.js에서 자바스크립트를 실행할 때 사용하는 엔진이며, 매우 빠른 성능을 자랑합니다. JIT는 이러한 성능을 구현하는 데 핵심적인 역할을 하고 있죠. 이제 JIT 컴파일의 개념과 V8에서 이를 어떻게 활용하는지에 대해 자세히 설명하겠습니다.

1. JIT 컴파일의 개념

JIT 컴파일은 프로그램을 실행하면서 필요한 시점에 즉석에서 코드를 컴파일하는 방식입니다. 일반적으로 프로그래밍 언어는 컴파일 언어와 인터프리터 언어로 나눌 수 있습니다.

  • 컴파일 언어(예: C, C++): 코드를 미리 컴파일하여 기계어로 변환 후 실행합니다. 따라서 실행 속도가 빠르지만, 컴파일하는 데 시간이 걸립니다.
  • 인터프리터 언어(예: Python, JavaScript): 코드를 한 줄씩 해석하며 실행합니다. 컴파일이 필요 없으나 실행 속도가 느릴 수 있습니다.

JIT 컴파일은 이 두 방식의 장점을 결합한 방식으로, 프로그램 실행 시 필요한 부분을 즉석에서 기계어로 변환해 최적화된 실행 성능을 얻습니다.

2. V8 엔진에서의 JIT 컴파일 동작 방식

V8 엔진은 JavaScript 코드를 효율적으로 실행하기 위해 여러 최적화 기법과 함께 JIT 컴파일을 활용합니다. V8의 JIT 컴파일 과정은 크게 두 단계로 나누어집니다:

(1) 인터프리팅과 기본 JIT 컴파일

V8은 처음에는 JavaScript 코드를 바로 기계어로 변환하지 않습니다. 대신, Ignition이라는 인터프리터가 JavaScript 코드를 바이트코드로 변환하여 해석하며 실행을 시작합니다. 이 단계에서는 코드가 반복되거나 최적화되지 않은 상태로 실행되기 때문에 비교적 성능이 낮습니다.

(2) 최적화된 JIT 컴파일

V8 엔진은 코드를 해석하는 과정에서 코드의 패턴을 관찰합니다. 즉, 자주 호출되거나 반복적으로 실행되는 코드가 발견되면, 해당 코드를 최적화하여 보다 빠르게 실행할 수 있도록 TurboFan이라는 최적화 컴파일러가 개입합니다. TurboFan은 자주 실행되는 코드를 기계어로 변환하여 캐시에 저장해두고, 이후에는 이 최적화된 기계어 코드로 실행합니다.

3. 최적화의 예시: V8의 히든 클래스와 인라인 캐싱

V8 엔진은 JIT 컴파일을 이용해 다음과 같은 최적화를 수행합니다:

  • 히든 클래스: V8은 자바스크립트의 동적 타입 특성을 다루기 위해 객체에 히든 클래스를 생성하여 내부적으로 추적합니다. 예를 들어, 객체의 프로퍼티 구조가 일정하면 그에 맞는 클래스를 만들어 최적화하여 빠르게 접근할 수 있도록 합니다.
  • 인라인 캐싱: 함수 호출이 자주 발생하는 경우, 해당 호출을 캐시에 저장해 빠르게 실행되도록 합니다. 이 최적화는 같은 함수가 반복 호출될 때 발생하는 성능 손실을 줄이는 데 큰 도움이 됩니다.

4. JIT의 단점과 극복 방안

JIT 컴파일은 코드가 실행되는 도중에 최적화가 일어나기 때문에 초기 실행 시 지연이 발생할 수 있습니다. 또한, 메모리를 많이 사용하기 때문에 리소스가 적은 환경에서는 과부하를 유발할 수 있습니다. 이러한 문제를 해결하기 위해 V8은 적절한 시점에 JIT 컴파일을 수행하도록 설계되었습니다. 또한, 메모리 사용량을 관리하기 위해 필요 없는 최적화 데이터를 정리하는 가비지 컬렉터가 주기적으로 동작합니다.

요약

  • V8의 JIT 컴파일은 JavaScript의 실행 성능을 높이기 위한 핵심 기술로, Ignition 인터프리터와 TurboFan 최적화 컴파일러가 함께 동작합니다.
  • 자주 호출되거나 반복되는 코드를 분석하여 최적화된 기계어 코드로 변환하여 실행 속도를 높이며, 히든 클래스와 인라인 캐싱을 통해 추가 최적화를 수행합니다.
  • JIT는 초기 실행 지연 및 메모리 사용 문제를 동반할 수 있지만, V8은 효율적인 메모리 관리와 가비지 컬렉션을 통해 이 문제를 해결하고 있습니다.

V8의 JIT 컴파일 덕분에 Node.js와 같은 JavaScript 환경에서 매우 빠른 실행 성능을 얻을 수 있습니다.

 

'NestJS' 카테고리의 다른 글

NestJS 핫리로드 트러블슈팅  (1) 2024.11.16
NestJS Pipe  (0) 2024.11.15
회원가입로직  (2) 2024.11.14
NestJS DI(Dependency Injection)  (0) 2024.11.06
NestJS 초기 파일들의 의미  (3) 2024.11.04
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유