Node JS 개념
웹 개발을 하기 위해 언어에 대한 개념부터 심화까지 공부하면서 실습까지 진행해 보자.
Node js는 Chroe V8 Javascript 엔진으로 빌드된 Javascript 런타임이다.
Node는 V8 엔진과 같이 libuv라는 라이브러리를 사용한다. V8과 libuv는 C와 C++로 구현되어 있다.
libuv 라이브러리는 노드의 특성인 이벤트 기반, 논 블로킹 I/O 모델을 구현하고 있다.
각 모델이 무엇이고 어떤 장단점들이 있는지 알아보자.
이벤트 기반
이벤트가 발생할 때 미리 지정해 둔 작업을 수행하는 방식을 의미한다. 이벤트는 클릭이나 네트워크 요청 등이 있을 수 있다.
이벤트 기반 시스템에는 특정 이벤트가 발생할 때 무엇을 할지 미리 등록해두어야 하는데 이를 이벤트 리스너에 콜백함수를 등록한다고 표현한다.
예를 들어 버튼을 클릭하면 경고창을 띄우도록 설정했다고 가정해 보자.
그럼 이것은 클릭 이벤트 리스너에 경고창을 띄우는 콜백 함수를 등록해 둔다고 이해하면 된다.
노드도 이벤트 기반 방식으로 동작하므로, 이벤트가 발생하면 이벤트 리스너에 등록해둔 콜백 함수를 호출한다.
발생한 이벤트가 없거나 이벤트를 다 처리하면 노드는 다음 이벤트가 발생할 때까지 대기한다.
이벤트 기반 모델에는 이벤트 루프라는 개념이 등장한다.
여러 이벤트가 동시에 발생했을 때 어떤 순서로 콜백 함수를 호출할지를 이벤트 루프가 판단한다.
노드는 자바스크립트 코드의 맨 위부터 한 줄씩 실행한다. 함수 호출 부분을 발견했다면 호출한 함수를 호출 스택에 넣는다.
function first() {
second();
console.log('first');
}
function second() {
third();
console.log('second');
}
function third() {
console.log('third');
}
first();
코드를 실행하면 위처럼 호출 스택이 push 되고 third() 함수부터 실행이 된다.
맨 밑에 anonymous는 처음 실행 시의 전역 컨택스트를 의미한다.
그럼 다음 코드는 실행 결과가 어떤지 예상해 보자.
function run() {
console.log('run after 3 seconds');
}
console.log('start');
setTimeout(run, 3000);
console.log('end');
예상한 결과처럼 결과가 나왔어요??
처음 접하는 분들은 예상과 다르게 나왔을 것이다. 이제 이 코드가 어떻게 작동하는지 같이 알아보자.
일단 위 코드의 작동방식을 분석하기 전에 알아야 할 개념이 있다.
이벤트 루프
이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백 함수의 실행 순서를 결정하는 역할을 담당
백그라운드
setTimeout 같은 타이머나 이벤트 리스너들이 대기하는 곳, 여러 작업이 동시에 실행될 수 있음
태스크 큐
이벤트 발생 후, 백그라운드에서는 태스크 큐로 타이머나 이벤트 리스너의 콜백 함수를 보냄, 정해진 순서대로 콜백들이 줄을 서 있음
호출 과정을 다음과 같다.
anonymous -> setTimeout 순서로 호출 스택에 들어간다. 그리고 setTimeout 함수가 먼저 실행이 되니까 run() 함수가 백그라운드로 들어간다. 그다음 anonymous가 호출 스택에 빠진다.
run() 함수는 백그라운드에서 3초를 센 뒤 태스크 큐로 들어간다. 그 후 호출 스택이 비워져 있는지 확인 후 비워져 있으면 run() 함수를 호출 스택에 넣고 실행한다.
그 결과 run() 함수의 콘솔이 제일 마지막에 실행이 된다.
논 블로킹
이벤트 루프를 잘 활용하면 오래 걸리는 작업을 효율적으로 처리할 수 있다.
작업에는 2가지 종류가 있다. 동시에 실행될 수 있는 작업과 동시에 실행될 수 없는 작업이다.
자바스크립트는 동시에 실행될 수 없지만 자바스크립트상에서 돌아가는 것이 아닌 I/O 같은 것은 동시에 처리될 수 있다.
논 블로킹은 이전 작업이 완료될 때까지 대기하지 않고 다음 작업을 수행한다는 뜻으로 비동기적 방식이라고 생각하면 된다. 반대로 블로킹은 이전 작업이 끝나야만 다음 작업을 수행한다는 뜻으로 동기적 방식이라고 생각하면 된다.
function taskFunc() {
console.log('Finish');
}
console.log('start');
taskFunc();
console.log('next');
위 함수의 결과는 무엇일까? 이 코드의 동작 방식은 어떤 방식일까?
자바스크립트는 위에서부터 순차적으로 코드를 진행한다. 그렇게 되면 블로킹으로 처리되면서 결과는 start, Finish, next가 될 것이다.
function taskFunc() {
console.log('Finish');
}
console.log('start');
setTimeout(taskFunc, 0);
console.log('end');
setTimeout을 사용하면 결과는 어떻게 될까?
setTimeout함수는 전형적인 논 블로킹 함수이다. 그래서 결과는 start, end, Finish가 될 것이다.
논 블로킹으로 코드를 작성하면 작업 시간을 절약할 수 있다.
싱글 스레드
노드는 싱글 스레드로 동작한다. 사실 엄밀히 말하면 싱글 스레드는 아니긴 한데 우리가 제어할 수 있는 스레드는 하나뿐이라 싱글 스레드라고 여겨진다.
서버로서의 노드
노드는 기본적으로 싱글 스레드, 논 블로킹 모델을 사용한다. 서버에는 기본적으로 I/O 요청이 많이 발생하므로, I/O 처리를 잘하는 노드를 서버로 사용하면 좋다.
노드는 libuv 라이브러리를 사용하여 I/O 작업을 논 블로킹 방식으로 처리한다. 따라서 스레드 하나가 많은 수의 I/O를 감당할 수 있다. 하지만 노드는 CPU 부하가 큰 작업에는 적합하지가 않다.
그래서 노드는 개수는 많지만 크기가 작은 데이터를 실시간으로 주고받는 데 적합하다.
네트워크나 데이터베이스, 디스크 작업 같은 I/O에 특화되어 있다. 실시간 채팅 애플리케이션이나 JSON 데이터를 제공하는 API 서버가 노드를 많이 사용한다.
노드 12 버전에서는 워커 스레드 기능의 안정화로 멀티 스레드 작업을 할 수 있게 되었지만, 멀티 스레드 프로그램을 하는 것은 싱글 스레드에 비해 어렵다.
노드의 큰 장점은 javascript를 사용한다는 것이다. 웹 브라우저도 자바스크립트를 사용하면서 서버까지 노드를 사용하면 하나의 언어로 웹 사이트를 개발할 수 있어서 다양한 기업들이 노드를 채택하게 되었다.
그리고 안정성과 보안성 측면의 문제도 이미 충분히 검증되어서 다양한 네이버, 우버, 넷플릭스, 월마트, 카카오 등 다양한 기업들이 노드를 사용한다.
다음 장에서는 node를 설치하고 기본적으로 node를 사용하는 방법을 알아보자.