상세 컨텐츠

본문 제목

Node.js 란 무엇인가? - Javascript Runtime의 이해

Node.js

by DeaKyungLee 2021. 7. 26. 23:41

본문

이 글은 Node.js에 대해서 다룬다.

개요

개발자로서 서버 개념을 너무나도 모른다는 생각에 시작한 Node.js 스터디이다.

현재까지 node.js는 그냥 막연하게 Back-End 에서도 JavaScript를 사용할 수 있도록 만들어주는 일종의 엔진 같은 개념이라 생각했다.

흔히 웹 개발에 많이 사용되며, 게임 개발에서는 그다지 등장하지 않는 용어이기도 하다. 게임 개발자를 항상 목표로 했기 때문에, 사실 그냥 관심이 없었다고 보는게 맞다.

더보기

[ 그런데 게임 서버와 웹 서버는 왜 그렇게 다른 개념일까? 단순히 브라우저랑 클라이언트 프로그램이라는 실행 환경의 차이가 있는거 아닌가?

이런 생각이 문득 들어서... 찾아보니, 아주 잘 정리해주신 글을 발견했다. ( 링크 ) ]

 

게임 서버 개발과 웹 서버 개발의 차이 · Elky Essay

늘 궁금했다. 웹 개발이란 어떤 것인지. 물론 이것저것 관심이 많다 보니, 임베디드, 보안, 인공지능 등 대부분 관심이 많지만, 좀 더 대중화 되고 컨텐츠 개발에서 주류에 있는 웹개발은 조금 더

elky84.github.io

 

 

What is Node.js

 

하여간 다시 돌아와서,

말 그대로 그냥 막연하게 JS를 백엔드에서도 사용할 수 있는 환경을 구성해주는 무언가? 정도로 생각한 것이다.

 

그런데 Node.js 공식 홈페이지에서는 뭐라고 설명할까?

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

Node.js®는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임입니다.
 

V8 JavaScript engine

What is V8? V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++. It is used in Chrome and in Node.js, among others. It implements ECMAScript and WebAssembly, and runs on Windows 7 or later, macOS 10.12+, and Linu

v8.dev

 

단순히 서버(백엔드)를 구성하게 해주는 JS 기반의 무언가는 아닌 것 같다.

일단 저 문장을 이해하려면 일단 먼저 서버와 런타임의 의미를 명확히 해야할 것이다.

서버는 간단히 말해서 요청(Request)에 따라서 응답(Response) 해주는 컴퓨터라고 생각하면 된다.

서버와 클라이언트의 관계

서버에 대한 개념은 자세히 말하자면 끝이 없고, 따로 검색하면 되기 때문에 현재는 이 정도만 개념적으로 알아둬도 무방하다.

 

Node.js®는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임입니다.

그렇다면 JavaScript 런타임이란 무엇인가?

특정 언어로 만든 프로그램들을 실행할 수 있는 환경을 말한다.

즉, Node.js는 일종의 JavaScript 실행기라고 말할 수 있다.

 

2008년 구글의 크롬 V8 엔진(오픈 소스 JS 엔진, C++ 기반)이 등장함에 따라서 노드 프로젝트가 시작되었다. 노드의 내부 구조에는 크롬 V8 엔진과 더불어서 libuv 라는 비동기 I/O 처리 및 이벤트 기반이라는 특성을 가진 C, C++ 라이브러리를 사용한다. 

사실상 libuv 라이브러리가 node의 특징인 이벤트 기반, None-Blocking I/O 모델을 구현하는 핵심이라고 볼 수 있다.

 

이벤트 기반, None-Blocking I/O

이번에는 이 두가지 개념을 명확히 해야한다.

사실 C -> C++ 순서로 프로그래밍에 입문한 사람에게는 둘 다 생소한 개념에 해당할 수 있다. ( 적어도 나는 그랬다. )

 

이벤트 기반

 

 먼저 이벤트 기반이란, 사용자로부터 특정한 Event(클릭, Request 등)를 받아서 특정 작업을 수행하는 방식을 말한다.

즉, 사용자의 특정 행동에 대한 작동 방식을 미리 지정해두는 방식이라고 말할 수 있다.

MFC, Unity 게임 엔진이나 전부 이벤트 기반이라고 말할 수 있다. ( 더 정확하게는 Unity 엔진은 이벤트 기반 처리도 가능하고 매 Frame 단위 체크 형식으로 처리가 가능하다. )

이벤트를 감지하고 ( Event Listener ), 해당 이벤트를 처리 ( Event Handler, Callback )하는 것이 이벤트 기반이다.

 

그렇다면 동시에 여러 이벤트가 발생하면 어떤 순서로 이벤트를 처리할 것인가?

이것을 판단하는 것이 바로 Event Loop 개념이며, 이는 호출 스택(Call Stack)과도 관련이 있다.

function first() {
  second();
  console.log('첫 번째');
}
function second() {
  third();
  console.log('두 번째');
}
function third() {
  console.log('세 번째');
}
first();

 

위의 코드로 해당 순서가 떠오르지 않는다면, 호출 스택 개념을 모르는 것이다.
크롬 개발자 도구에서 실제 Callstack 확인

여기서 "annoymous"는 Javascript의 익명 함수 ( annoymous function ) 의 그 개념이 맞다.

보다시피 따로 선언한 first, second, third 함수와는 다르게 자기가 알아서 스택 가장 밑바닥에 들어가 있다.

이것은 c, c++의 main과 비슷한 개념인데,

해당 코드 전체에 해당하는 JS 파일을 실행하는 것이 선결되어야 first() 함수가 실행될 수 있기 때문이다.

( 이것을 Global Execution Context 이라고 말한다. )

 

여기서 익명 함수에 대해서 모른다면 링크 참고

 

익명 함수(Anonymouse Function)에 대하여

익명 함수(Anonymouse Function) 한 마디로 이름이 없는 함수, 람다식(Lambda Expression) 이라고도 부른다. 코드를 보면 그냥 한 번에 감이 온다. // 일반적인 방식 function NamedFunc(){ console.log("일반..

daklee.tistory.com

 

 

그런데 호출 스택만으로는 설명이 안되는 함수 호출이 setTimeout 같은 함수이다.

 

function run(){
    console.log('3초 후 실행!');
}

console.log("시작");
setTimeout(run, 3000);
console.log("끝");

실행 결과

3초후에 실행되는 run은 대체 언제 호출 스택에 들어갔고, 어디서 실행된걸까?

이것을 설명하려면 백그라운드(Background), 태스크 큐(Task Queue) 개념에 대해서 추가적으로 알아야한다.

추가적으로 Javascript EngineEvent Loop에 대해서 다시 정리해보자.

 

Javascript Runtime

 

Javascript Engine : 앞에서 언급한 구글 크롬 V8 엔진이 대표적이다. 하지만 그것외에도 굉장히 많은 JS 엔진들이 존재한다.

현역에 속하는 JS 엔진 종류 - Wiki

JS 엔진의 역활은 크게 3가지로 나눌 수 있다.

1. 코드를 읽고 해석하는 Interpreter

2. 코드를 바탕으로 현재 실행중인 서브루틴을 관리하는 Call Stack

3. 변수와 객체에 대한 메모리 할당 및 관리를 담당하는 Heap Memory

 

자바스크립트 엔진

보시다시피, 메모리 설정 및 호출 순서 결정을 통해서 코드를 해석해서 직접적으로 실행시키는 곳이다.

그리고 놀라운 사실은 엔진의 종류에 따라서 인터프리터 뿐만 아니라, 필요에 따라 Just-In-Time 형식으로 컴파일 과정까지 수행한다. ( V8 엔진이 대표적인 예시 )

 

Background : 타이머나 이벤트 리스너들이 대기하는 장소이며 JS가 아닌 다른 언어로 작성된 프로그램이다. 동시에 여러 작업이 실행 될 수 있다. ( 실행 환경에 따라 OS, Web Browser 등이 될 수 있다. ) 즉, Web Browser 환경에서 실행된다면 Web API가 실행되는 곳이라고 생각해도 된다.

 

Task Queue(callback queue) : 이벤트 리스너에 의해 실행될, 실제 어떤 수행을 하는지를 나타내는 Event Handler( Callback 함수 )가 모여있는 Queue 이다. ( 때문에 Callback Queue라고도 부른다. ) Microtask Queue, Animation Frames 등 여러개의 큐로 이루어져 있다.

참고로, 타이머나 이벤트 리스너를 실행하면 태스크 큐로 해당 이벤트 리스너 콜백 함수를 보내는 곳이 Background 가 되는 것이다.

 

Event Loop : 이벤트 발생 시 호출할 콜백 함수들을 관리하고, 실행 순서를 결정한다. JS 엔진의 Call Stack과 Task Queue의 중간에 있는 관리자 개념에 해당한다. 만약 Call Stack이 비어있다면, Task Queue에서 가장 첫 번째 Callback을 Call Stack으로 밀어 넣는다. ( 이러한 한 번의 과정을 Tick 이라 한다. ) 여기서 예측할 수 있다시피, 만약 Call Stack이 다 차있다면? 당연히 Task Queue에서 당장 실행되어야할 Callback 함수가 있다고 하더라도 Call Stack으로 들어가지 못 한다. 이것이 setTimeout 같은 함수가 정확하지 않을 수 있는 이유이다.

더 자세하게 말하자면 JS가 싱글 스레드 기반이라 비동기 처리를 활용한다는 것과 Callback Event Queue 개념 및 FIFO 개념에 대해서 알아야한다. 해당 개념에 대한 링크

 

이벤트 루프란?

- 이벤트 루프란 무엇인가요? Callback Event Queue에서 하나씩 꺼내서 동작시키는 Loop를 말합니다. 자바스크립트는 단일 스레드 기반 언어이기 때문에, 한번에 하나씩 작업을 진행한다. 그러나 자바

zereight.tistory.com

 

 

Javascript Engine, Background, Task Queue, Event Loop

이러한 4가지 개념으로 구성되는 것이 바로 Javascript Runtime 이다.

JS 런타임 구조 - "How JavaScript works: an overview of the engine, the runtime, and the call stack"

 

앞서 하나씩 언급했다시피,

(1) Memory Heap + Call Stack 으로( = Interpreter) 구성된 JS 엔진 ( 주로 V8 엔진 )

(2) 이벤트 리스너에 의해 실행되어서 해당 이벤트에 실제로 어떤 수행을 할지를 결정하는 이벤트 핸들러(Callback) 를 모아둔 Callback Queue ( Task Queue )

(3) 그리고 엔진의 Call Statck 과 Callback Queue 사이의 관리자 역활을 하는 Event Loop

(4) 마지막으로 외부 함수로서 이벤트 리스너가 모여있는 Background ( Web API... )

Javascript Runtime을 요약하자면 위와 같다.

 

None Blocking I/O

 

싱글 스레드

댓글 영역