Node.js
는Chrome V8 Javascript
엔진으로 빌드된 자바스크립트 런타임이다.
런타임이란 특정 언어로 만든 프로그램을 실행할 수 있는 환경을 뜻한다. 즉, 노드는 자바스크립트 실행기이다.
노드는 V8
엔진과 libuv
라이브러리를 사용한다.
이벤트 기반과 논블로킹 I/O 모델을 구현하고 있다.
이벤트 기반(event-driven)이란 클릭이나 네트워크 요청 등의 특정 이벤트에 따라 작업을 수행하는 방식이다. 각 이벤트마다 실행될 작업을 미리 등록해둬야 한다. 이러한 작업을 이벤트 리스너(event listener)에 콜백(callback) 함수를 등록한다
고 한다.
여러 이벤트는 이벤트 루프(event loop)로 관리된다. 동시에 발생했을 경우 콜백 함수의 호출 순서를 판단하는 것이다. 노드는 기본적으로 JS
코드를 위에서 아래로 한 줄씩 실행한다.
function A(){
B();
console.log("A");
}
function B(){
C();
console.log("B");
}
function C(){
console.log("C");
}
A();
/* 결과
C
B
A
*/
함수는 호출된 순서대로 호출 스택에 쌓이고, 역순으로 실행되며, 실행이 완료되면 스택에서 지워진다. 호출 스택에는 전역 콘텍스트인 anonymous
, A
, B
, C
순서로 저장되고, 가장 위에 있는 C
부터 실행되며 위와 같은 결과가 나타난다.
시간 지연이 있는 콜백 함수의 경우에는 조금 달라진다.
function run(){
console.log("3초 후 실행");
}
console.log("시작");
setTimeout(run, 3000);
console.log("끝");
/* 결과
시작
끝
3초 후 실행
*/
시작
이후에 호출된 setTimeout
이 이벤트 루프에 의해 호출 스택에 들어가 실행된다. 콜백 함수 run
과 타이머를 백그라운드로 보낸다. 백그라운드는 이벤트 리스너가 대기하는 곳이다. run
은 3초 후 태스크 큐로 보내진다. 호출 스택이 비어 있으면 이벤트 루프는 태스크 큐로부터 함수를 하나씩 가져와 호출 스택에 넣고 실행한다. 단, 태스크 큐는 호출 스택이 비어 있어야만 큐의 함수를 호출 스택에 올리므로, 호출 스택에 많은 함수가 대기 중이라면 3초 후에도 run
이 실행되지 않을 수 있다.
자바스크립트 코드는 동시에 실행될 수 없다. 하지만 입출력(I/O) 작업은 동시 처리가 가능하다. 파일 쓰기/읽기, 폴더 생성 혹은 네트워크 요청 등이 I/O
의 일종이며, 노드는 이러한 작업을 논블로킹 방식으로 처리한다. 논블로킹이란 이전 작업 완료를 기다리지 않고 다음 작업을 수행하는 것이다. 동시에 처리할 수 있는 작업은 최대한 묶어 백그라운드로 넘겨야 시간을 절약할 수 있다.
오래 걸리는 작업이 있는 함수의 경우 다음처럼 setTimeout
에 넘겨 논블로킹을 구현한다.
function longWorkingTask(){
// 오래 걸리는 작업
console.log("작업 끝");
}
console.log("작업 시작");
setTimeout(longWorkingTask, 0);
console.log("다음 작업");
/* 결과
작업 시작
다음 작업
작업 끝
*/
setTimeout(callback, 0)
은 논블로킹으로 만들기 위해 사용하는 기법 중 하나라고 한다. 콜백인 longWorkingTask
가 태스크 큐로 넘어가므로 호출 스택의 모든 작업이 완료된 후 longWorkingTask
가 실행된다.
참고로, 논블로킹이 동시성을 의미하지는 않는다. 동시에 처리할 수 있는 작업들을 논블로킹으로 처리해야 동시성을 얻을 수 있다.
자바스크립트가 동시에 실행될 수 없는 이유는 싱글 스레드(single thread)로 동작하기 때문이다. 하지만 노드는 싱글 스레드로 동작하지 않는다. 노드 실행 시 생성되는 프로세스는 내부적으로 여러 스레드를 갖는다. 단, 프로그래머가 제어할 수 있는 스레드는 하나뿐이어서 싱글 스레드로 여겨진다.
스레드 풀
과워커 스레드
는 노드에서 멀티 스레드로 동작하는 경우이다. 전자는 암호화나 입출력 등을 특정 작업을 수행할 때 노드 스스로 멀티 스레드 기능을 사용한다. 후자는 노드 12부터 안정화된 기능으로, 필요하다면 프로그래머가 멀티 스레드를 다룰 수 있다.
I/O
작업이 많은 서버로 적합JSON
형식과 쉽게 호환