브라우저 환경

JavaScript를 실행하려면 JS 엔진이 필요한데 브라우저에는 기본적으로 JS 엔진이 탑재되어 있다.

 

Chrome - V8

Firefox - SpiderMonkey

Safari - JavascriptCore

Internet Explorer - Chakra

 

최초의 JS 엔진은 단순한 interpreter이었지만 위와 같은 최신 JS 엔진들은 성능 문제로 JIT Compiliation(Just-In-Time)을 사용한다.

 

JIT Compilitation이란?

Interpreter : 코드를 한줄씩 번역 후 실행 => interpreter는 코드를 한줄씩 해석하고 실행하기 때문에 heavy한 기능을 수행하기에는 너무 느림

Compiler : 코드를 한번에 기계어로 변환한 뒤 실행

JIT Compiliation : 코드가 실행되는 시점에 실시간으로 interpreter 방식으로 기계어 코드 생성, 해당 코드가 컴파일 대상이 되면 코드를 컴파일하고 캐싱함. 동일 코드 다시 나오면 컴파일하지 않고 캐싱한 부분 재사용

interpreter의 장점(한줄씩) + compiler의 장점(캐싱-컴파일한 부분 저장해두는 것)을 합친 방식이라 보면 됨

 

브라우저 밖의 외부 환경

runtime(환경)이란?

런타임이란 프로그래밍 언어가 구동되는 환경을 말한다. 여러 웹 브라우저들도 JS의 런타임 환경이 되고 웹 브라우저 외부에서는 Node.js 와 같은 런타임을 사용한다. Node.js는 프로그래밍 언어도, 프레임워크도 아닌 자바스크립트 런타임이다. 

출처: https://goldenrabbit.co.kr/2024/03/19/node-js-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A1%9C-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%9E%85%EB%AC%B8%ED%95%98%EA%B8%B0-%E2%9D%B6/

 

Node.js 시스템은 위와같이 V8, Node.js API, Node.js 바인딩, Libuv로 구성되어 있다.

V8: 자바스크립트 코드를 실행하는 엔진으로 1+1과 같은 간단한 작업을 실행한다.

Node.js API: 파일 시스템. http, crypto 암호화 처리 등 복잡한 기능을 수행하는 여러 api, 이 api들은 자바스크립트로 작성된 것도 있지만 C++이나 C와 같은 low level 언어로 작성된 것도 있다.

Node.js 바인딩: V8에서 처리하지 못하는 C, C++, 파일시스템 등과 같은 코드들은 libuv에서 처리해야됨. Node.js 바인딩이 이 libuv에 작업을 넘겨주는 역할을 수행한다. 

Libuv: event loop를 기반로 비동기 I/O task를 처리하는 다중 플랫폼  C 라이브러리다. 플랫폼 별로 가장 빠른 비동기 IO 인터페이스로 통일된 코드를 돌릴 수 있다는 장점이 있다. 이는 특정 함수를 실행할 때 플랫폼(widow, mac 등)별로 따로 코드를 짜야되지만 libuv는 그럴 필요 없이 libuv 하나만 있으면 알아서 플랫폼에 최적화된 코드를 실행해준다는 의미이다.

Node.js는 v8이 코드를 해석하고 Node.js API들 중 하나의 함수를 호출한 뒤 Node.js 바인딩이 libuv에서 원하는 작업을 처리하게 하는 방식으로 동작한다(API에서 바인딩을 호출하고 바인딩에서 libuv를 호출하는 구조).

 

libuv는 어떠한 방식으로 동작하는가?

node.js는 자바스크립트 언어를 사용하는데 자바스크립트 언어는 싱글스레드이기 때문에 한 번에 하나의 작업만 처리할 수 있다. 그렇다면 node.js는 비동기로 여러 작업을 한번에 수행할 수 없는 것일까?

그렇지 않다! node.js는 libuv에 있는 Event loop를 통해 비동기로 여러 작업을 처리할 수 있다. 작업 처리의 순서는 아래와 같다.

1, 우선 V8엔진에서 libuv로 전달된 작업은 libuv 내부 이벤트 큐에 추가된다.

2. 이벤트 큐에 쌓인 요청은 차례대로 이벤트 루프에 전달되고 운영체제 커널에 넘겨져 비동기 처리 작업이 수행된다(논블로킹 처리).

3. 운영체제 내부적으로 비동기 처리가 힘든경우(DB, DNS lookup, Crypto, 파일처리 등) 스레드 풀에 넘겨져 작업이 수행된다(블로킹 처리).

4. 완료된 작업은 이벤트 루프로 전달되고 이벤트 루프에서는 콜백으로 전달된 요청에 대한 완료 처리를 한 뒤 호출된 부분으로 다시 넘긴다.

이러한 과정을 통해 node.js는 비동기 작업을 수행한다.

근데 위 그림을 보면 블로킹 처리, 논블로킹 처리가 모두 존재하는데 이 부분이 살짝 헷갈린다. 

출처: 패스트캠퍼스 10개 프로젝트로 끝내는 Node.js의 모든 것(Express & Nest.js) 초격차 패키지 Online 강의

 위 그림에서 main thread는 event loop를 담당하는 일꾼이다. 이는 운영체제 커널에서 지원하는 비동기 작업들을 받으면 커널의 비동기 함수들을 호출한다. 하지만 커널에서 지원하지 않는 작업이나 이벤트 루프를 블로킹할 수 있는 시간이 오래 걸리는 몇몇 작업들이 들어오면 스레드 풀의 워커 스레드에 넘겨준다. 위 그림에서 main thred를 제외한 나머지 부분이라고 보면 되겠다. 워커스레드는 기본적으로 4개로 구성되어 있고 128개까지 늘어날 수 있다. 만약 워커 스레드가 4개일 때 5개 이상의 작업이 들어오면 스레드가 작업이 끝날 때까지 대기해야 한다. 이 때문에 위위 그림에서 블로킹 처리라고 적어놓은게 아닐까 싶다. 

 

요약

- 코드가 호출 스택에 쌓인 후 실행되는데 그 작업이 비동기 작업이면 이벤트 루프는 OS커널 or 스레드풀에 작업 위임

- 비동기 작업 수행 뒤 콜백함수 호출

 

다음 글에서는 event loop가 무엇인지 자세히 알아봐야겠다.

 

 

 

https://ko.wikipedia.org/wiki/JIT_%EC%BB%B4%ED%8C%8C%EC%9D%BC

 

JIT 컴파일 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. JIT 컴파일(just-in-time compilation) 또는 동적 번역(dynamic translation)은 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일 기법이다. 전통적인 입장에서 컴

ko.wikipedia.org

https://hyeinisfree.tistory.com/26

 

[Java] JIT 컴파일러란?

JIT 컴파일(just-in-time compilation) 또는 동적 번역(dynamic translation)은 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일 기법이다. 컴파일러 vs 인터프리터 컴파일러와 인터프리터 모두 high

hyeinisfree.tistory.com

https://joe-cho.tistory.com/16

 

Libuv 라이브러리(feat. 이벤트 루프) - 3

✏️ Node.js ➡️ 네트워크 서버 구축에 특화되어 있고, V8엔진은 기존의 인터프리터 언어인 자바스크립트를 컴파일 방식으로 처리해 빠른 속도로 작업을 수행할 수 있도록 합니다. 또한, Livub 라

joe-cho.tistory.com

 

'NodeJS' 카테고리의 다른 글

[Node.js] Event Loop 이해하기  (4) 2024.09.20

+ Recent posts