Node란?
노드는 자바스크립트 런타임이다. 런타임은 특정 언어로 만든 프로그램들을 실행할 수 있는 환경을 뜻한다.
따라서 노드는 자바스크립트 프로그램을 컴퓨터에서 실행할 수 있다.
노드는 구글이 출시한 V8 엔진과 더불어 libuv라는 라이브러리를 사용한다. V8과 libuv는 C와 C++로 구현되어 있다.
따라서 자바스크립트 코드를 노드가 알아서 V8과 libuv에 연결해준다.
libuv 라이브러리는 노드의 특성인 이벤트 기반, 논 블로킹 I/O 모델을 구현하고 있다.
이벤트 기반
이벤트 기반이란 이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식을 의미한다. 이벤트로는 클릭이나 네트워크 요청 등이 있다. 이벤트 기반 시스템에서는 특정 이벤트가 발생할 때 무엇을 할지 미리 등록해두어야 한다.
이를 리스너, 콜백 함수를 등록한다고 표현한다.
이벤트 기반 모델에서는 이벤트 루프(event loop)라는 개념이 등장한다. 여러 이벤트가 동시에 발생했을 때 어떤 순서로 콜백 함수를 호출할지를 이벤트 루프가 판단한다. 이벤트 루프는 이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백 함수의 실행 순서를 결정하는 역할을 한다.
백그라운드와 태스크 큐를 이용하여 콜백 함수들을 관리한다. 이벤트 루프는 호출 스택이 비어 있으면 태스크 큐에서 함수를 하나씩 꺼내어 실행한다. 따라서 백그라운드에서 setTimeout을 사용하여 타이밍을 조절한다 해도 호출 스택이 비어있지 않으면 원하는 타이밍에 함수를 실행하지 못할 수 있다. 이것이 setTimeout의 시간이 정확하지 않을 수 있는 이유이다.
논 블로킹 I/O
이벤트 루프를 잘 활용하면 오래 걸리는 작업을 효율적으로 처리할 수 있다. 작업에는 두 가지 종류가 있다.
동시에 실행될 수 있는 작업과 동시에 실행될 수 없는 작업이 있다. 기본적으로 자바스크립트 코드는 동시에 실행될 수 없다. 하지만 I/O 같은 작업은 동시에 처리될 수 있다. 이를 논 블로킹 방식으로 처리한다라고 한다.
setTimeout(콜백, 시간)은 논 블로킹으로 만들기 위해 사용하는 기법 중 하나이다. 콜백 안에 오래 걸리는 작업을 작성한다면 태스크 큐로 보내지므로 아래에 있는 빠른 처리가 가능한 작업부터 처리를 할 것이다. 하지만 동시에 실행하는 게 아니라 단지 순서만 바꾸는 것이다. 따라서 전체 시간이 짧아지지는 않는다.
싱글 스레드
노드의 유저들이 겪고 있는 고질적인 문제이다. 스레드를 하나밖에 사용하지 못한다.
노드를 실행하면 프로세스가 하나 생성된다. 그리고 이 프로세스의 여러 개의 스레드가 생성된다. 하지만 우리가 제어할 수 있는 것은 단 하나이다.
노드가 싱글 스레드로 동작하지 않는 경우는 두 가지가 있다. 하나는 스레드풀(Thread Pool)이고, 다른 하나는 워커 스레드(Worker Thread)이다. 스레드풀은 노드가 특정 동작을 수행할 때 스스로 멀티 스레드를 사용한다.
워커 스레드는 노드 12 버전에서 안정화된 기능으로 노드에서도 멀티 스레드를 사용할 수 있게 만든 것이다.
서버로서의 노드
노드는 기본적으로 싱글 스레드, 논 블로킹 모델을 사용한다. 따라서 앞에서 설명한 장단점과 크게 다르지 않게 장단점을 갖는다. 서버에는 기본적으로 I/O 요철이 많이 발생한다. I/O 처리를 하는데 노드의 장점은 빛난다. 논 블로킹 방식으로 처리하기 때문이다.
하지만 CPU 부하가 큰 작업에는 적합하지 않다. 스레드 하나에서 처리되기 때문에 코드가 CPU 연산을 많이 요구하면 스레드 하나가 감당하기 어렵다.
이런 특성으로 노드는 개수는 많지만 크기는 작은 데이터를 실시간으로 주고받는 데 적합하다. 네트워크나 데이터베이스, 디스크 작업 같은 I/O에 특화되어 있다. 실시간 채팅 앱, 주식 차트, JSON 데이터를 제공하는 API 서버가 노드를 많이 사용한다. 멀티 스레드 기능이 있다 해도 CPU를 많이 사용하는 작업을 위한 서버로는 적합하지는 않다.
굳이 하고 싶다면 AWS 람다나 구글 클라우드 펑션스같은 서비스에서 노드로 CPU를 많이 사용하는 작업을 처리하는 것을 지원하는 것을 고려해봐야 한다.