공식사이트에서는 다음과 같이 노드를 설명한다.
Node.jsⓇ는 Chrome V8 Javascript 엔진으로 빌드된 Javascript 런타임입니다.
서버라는 말은 없는 이유는 서버만 실행할 수 있는 것이 아니기 때문! 그렇다면? 서버 외의 자바스크립트 프로그램을 실행하는 런타임으로서 사용할 수도 있다. 근데 서버와 런타임은 무엇일까?
노드를 통해서 다양한 자바스크립트 어플리케이션을 실행할 수 있지만 노드는 서버 어플리케이션을 실행할 때 가장 많이 사용한다. 그렇다면 서버란 무엇일까? 서버는 네트워크를 통해 클라이언트에 정보나 서비스를 제공하는 컴퓨터 혹은 프로그램. 클라이언트란 요청을 보내는 주체. 브라우저, 프로그램, 앱, 다른 서버일수도.
노드는 자바스크립트 프로그램이 서버로서 기능하기 위한 도구를 제공해서 서버 역할을 수행할 수 있다. 근데 왜 다른 언어가 아니라 노드를 이용해서 서버를 만들까? 노드의 특성을 알아보자!
노드는 자바스크립트 런타임. 런타임은 특정 언어로 만든 프로그램을 실행할 수 있는 환경. 쉽게, 노드는 자바스크립트 실행기. 기존에는 자바스크립트 프로그램을 웹 브라우저 위에서만 실행할 수 있었다. 브라우저가 자바스크립트 런타임을 내장하고 있었기에. 이전에 브라우저 외의 환경에서 자바스크립트를 실행하기 위한 시도가 있었지만 자바스크립트 실행 속도로 인해서 큰 호응을 얻지 못했다.
하지만 2008년 구글이 V8 엔진을 통해 크롬을 출시! 당시 V8 엔진은 다른 자바스크립트 엔진과 달리 매우 빨랐고 오픈 소스였다. 속도 문제가 해결되자 라이언 달은 2009년 V8 기반의 노드 프로젝트를 시작했다.
노드는 V8과 더불어 libuv라는 라이브러리를 사용. C와 C++로 구현되어 있따. libuv는 이벤트 기반, 논 블로킹 I/O 모델을 구현하고 있다. 컴구조를 떠올리자!
이벤트 기반이란 이벤트가 발생할 때마다 지정해둔 작업을 수행하는 방식을 의미. 이벤트로는 클릭, 네트워크 요청 등. 특정 이벤트가 발생할 때 무엇을 할지 미리 등록해두어야 한다. 이를 event listener, callback 함수를 등록한다고 표현.
ex) 클릭 이벤트 리스터에 경고장을 띄우는 alert 콜백함수를 등록해두면, 이벤트 리스너에 등록해둔 콜백함수를 호출. 이벤트가 없거나 다 처리하면 노드는 다음 이벤트가 발생할 때까지 대기한다.
이벤트 기반 모델에서는 이벤트 루프라는 개념이 등장. 여러 이벤트가 동시에 발생했을 때 어떤 순서로 콜백 함수를 호출할 지 이벤트 루프가 판단.
노드는 자바스크립트 코드의 맨 위에서부터 한 줄씩 실행. 함수 호출 부분을 발견했다면 호출한 함수를 호출 스택(call stack)에 넣는다. 다음 문제 풀어보기!
function first() {
second();
console.log('첫 번째');
}
function second() {
third();
console.log('두 번째');
}
function third() {
console.log('세 번째');
}
first();
그림 1-5에서 anonymous 함수는 처음 실행 시의 전역 컨텍스트(global context)를 의미. 컨텍스트는 함수가 호출되었을 때 생성되는 환경을 의미. 자바스크립트 코드는 실행 시 기본적으로 전역 컨텍스트 안에서 돌아간다. 함수는 실행되는 동안 호출 스택에 머물러 있다가 호출 스택에서 지워진다.
특정 밀리초 이후에 코드를 실행하는 setTimeout을 사용하는 예제.
function run() {
console.log('3초 후 실행');
}
console.log('시작');
setTimeout(run, 3000);
console.log('끝');
3초 뒤에 run함수를 실행하는 코드. 실행컨텍스트에서 배웠닷! 스택만으로는 설명하기가 어렵다. 이를 파악하기 위해서는 이벤트 루프, 태스크 큐(task queue), 백그라운드(background)를 알아야 한다.
추상화를 돕는 내용
- 이벤트 루프: 이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백함수의 실행 순서를 결정하는 역할을 담당. 노드가 종료될 때까지 이벤트 처리를 위한 작업을 반복하므로 루프(loop)라고 부른다.
- 백그라운드 : setTimeout 같은 타이머, 이벤트 리스너 등이 대기하는 곳. 자바스크립트가 아닌 다른 언어로 작성된 프로그램이라고 봐도 됨. 여러 작업이 동시에 실행될 수 있다.
- 태스크 큐 : 이벤트 발생 후, 백그라운드에서는 태스크 큐로 타이머, 이벤트 리스너의 콜백함수를 보낸다. 정해진 순서대로 콜백이 줄을 서 있어 콜백 큐라고 부르기도.
이벤트 루프를 잘 활용하면 오래 걸리는 작업을 효율적으로 처리할 수 있다. 작업에는 두 가지 종류가 있는데, 동시에 실핼될 수 있는 작업과 동시에 실행할 수 없는 작업. 기본적으로 여러분들이 작성하신 자바스크립트 코드는 동시에 실행될 수 없습니다. 하지만 자바스크립트 상에서 돌아가는 것이 아닌 I/O 작업 같은 것은 동시에 처리될 수 있다.
I/O와 연관된 작업을 할 때 노드는 논 블로킹 방식으로 처리하는 방법을 제공한다. 논블로킹이란 이전 작업이 완료될 때까지 대기하지 않고 다음 작업을 수행할을 뜻합니다. 반대로 블로킹은 이전 작업이 끝나야만 다음 작업을 수행하는 것을 의미.
컴퓨터 구조에서 배웠다! 요렇게 시각화해서 보지는 않았지만 파이프라인과 비슷한 느낌이군!!
노드 I/O 작업을 백그라운드로 넘겨 동시에 처리하곤 합니다. 따라서 동시에 처리될 수 있는 작업들은 최대한 묶어서 백그라운드로 넘겨야 시간을 절약할 수 있다.
순서의 중요성을 알 수 있다. 처리하는데 1초가 걸리는 작업 다섯 개가 있는데, 그 중 세 개의 동시에 처리할 수 있꼬 두 개는 동시에 처리할 수 없습니다. case 1과 같은 순서대로 작업하려면 5초가 소요될 것입니다. 같은 양의 작업을 case2 처럼 순서만 바꾸면 3초 정도로 작업 시간이 단축.
이렇게 작업 순서에 따라서 성능이 크게 달라진다. 동시에 처리될 수 있는 I/O 작업이라도 논 블로킹 방식으로 코딩하지 않으면 의미가 퇴색! 습관을 잘 들입시다. 블로킹과 논 블로킹 외에 동기와 비동기도. 동기와 비동기, 블로킹과 논블로킹의 관계는 이후에 3장에서 살펴볼 예정. 노드에서는 동기와 블로킹이 유사하고 비동기오 논 블로킹이 유사하다!
이벤트 기반, 논 블로킹 모델과 더불어 노드를 설염할 때 자주 나오는 용어가 바로 싱글 스레드. 싱글 스레드란 스레드가 하나 뿐이라는 것을 의미. 프로세스부터 이해해보자.
노드는 싱글스레드. 그렇지만 싱글 스레드로 동작하지 않음. 먼저 프로세스가 하나 생성되고 그 프로세스에서 스레드들을 생성. 내부적으로 스레드를 여러 개 생성. 직접 제어할 수 있는 스레드는 하나 뿐. 그래서 흔히 노드를 싱글 스레드라고 취급하는 것!
노드는 기본적으로 싱글 스레드, 논 블로킹모델을 사용하므로(자바스크립트 언어의 특성), 노드 서버 또한 동일한 모델일 수 밖에 없다. 따라서 노드 서버의 장단점은 싱글 스레드, 논 브로킹 모델과 크게 다르지 않다.
서버에는 기본적으로 I/O 요청이 많이 발생하므로, I/O 처리를 잘하는 노드를 서버로 사용하면 좋다. 노드는 논블로킹 방식으로 코드를ㄹ 작성했다는 가정하에 libuv 라이브러리를 사용하여 I/O 작업을 논 블로킹 방식으로 처리한다. 따라서 스레드 하나가 많은 수의 I/O를 혼자서도 감당할 수 있다. 하지만 노드는 CPU 부하가 큰 작업에는 적합하지 않는다. 우리가 작성하는 코드는 모두 스레드 하나에서 처리됨. 코드가 CPU 연산을 많이 요구하면 스레드 하나가 혼자서 감당하기 어렵다.
노드는 그래서 개수는 많지만 크기는 작은 데이터를 실시간으로 주고 받는 데 적합. 네트워크, DB, DISK 작업 같은 I/O에 특화되어 있기 때문. 실시간 채팅 애플리케이션, 주식차트, JSON 데이터 제공하는 API 서버가 노드를 많이 사용.
멀티 스레드 기능이 있다고 하더라도 이미지나 비디오 처리, 대규모 데이터 처리처럼 CPU를 많이 사용하는 작업을 위한 서버로는 권장x ➡️ 다른 언어가 더 적합하며 굳이 노드로 하려면 AWS lambda, Google Cloud Functions 같은 서비스 이용. 싱글 스레드 방식으로 서버를 운영 시, 하나 뿐인 스레드가 에러로 인해 멈추지 않도록 잘 관리해야 한다. 에러를 제대로 처리하지 못하면 하나 뿐인 스레드가 죽어버려 서버 전체가 멈춤.
노드 웹 서버가 내장. 하지만 나중에 서버 규모가 커지면 결국 nginx 등의 웹 서버를 노드 서버와 연결해야만. 노드는 생산성은 매우 좋지만 Go처럼 비동기에 강점을 보이는 언어이지만 nginx처럼 정적 파일 제공, 로드 밸런싱에 특호된 웹 서버에 비해서는 속도가 느리다.
장점 | 단점 |
---|---|
멀티 스레드 방식에 비해 적은 컴퓨터 자원 사용 | 기본적으로 싱글 스레드라서 CPU 코어를 하나만 사용 |
I/O 작업이 많은 서버로 적합 | CPU작업이 많은 서버로는 부적함(이미지, 비디오) |
멀티 스레드 방식보다 쉽다 | 하나 뿐인 스레드가 멈추지 않도록 관리가 필요 |
웹 서버가 내장되어 있음 | 서버 규모가 커졌을 때 서버를 관리하기 어려움 |
자바스크립트를 사용함 / JSON 형식과 쉽게 호환됨 | 어중간한 성능 |
정적인 콘텐츠를 많이 제공하는데 장점이 뚜렷하지는 앟지만 적합하지 않은 것도 아니다. 넌적스, 퍼그, EJS 같은 템플릿 엔진을 통해 다른 언어와 비슷하게 콘텐츠를 제공. 안정성과 보안성 측면의 문제도 충분히 검증되어 있다.