node.js는 언어나 프레임워크가 아니다. node.js는 Chrome V8 자바스크립트 엔진으로 빌드된 자바스크립트 런타임이다. 즉 자바스크립트를 브라우저 바깥에서도 실행할 수 있도록 해주는 자바스크립트 실행기이다.
node.js는 이벤트 기반 방식으로 동작한다. 이벤트가 발생하면 이벤트 리스너에 등록해둔 콜백함수를 호출한다.
여러 이벤트가 동시에 발생한다면 어떤 순서로 콜백함수를 실행할지 이벤트 루프가 판단한다.
node.js는 내부적으로 이벤트 루프, 백그라운드, 태스크 큐를 통해 동작한다. 브라우저에서 자바스크립트도 같은 동작방식으로 작동한다.
코드를 보며 node.js의 동작을 이해해보자.
function run() {
console.log('3초 후 실행')
}
console.log('시작')
setTimeout(run, 3000)
console.log('끝')
위 코드는 node.js에서 내부적으로 아래 그림처럼 동작한다.
먼저 전역 컨텍스트인 main()이 호출 스택에 들어가고 그 뒤에 setTimeout이 호출 스택에 들어간다.
호출 스택에 들어간 순서와 반대로 실행되므로, setTimeout이 먼저 실행된다.
setTimeout이 실행되면 타이머와 함께 run 콜백을 백그라운드로 보내고, setTimeout은 호출 스택에서 빠진다.
그 다음으로 main()이 호출 스택에서 빠진다.
백그라운드에서는 3초를 센 후 run 함수를 태스크 큐로 보낸다.
이벤트 루프에서는 태스크 큐의 콜백 함수들을 정해진 규칙에 따라 하나씩 호출 스택으로 부른다.
호출 스택의 run이 실행되게 된다.
만약 호출 스택에 함수들이 너무 많이 들어 있으면 3초가 지난 후에도 run 함수가 실행되지 않을 수 있다. 이벤트 루프는 호출 스택이 비어 있을 때만 태스크 큐에 있는 run 함수를 호출 스택으로 가져온다. 이 때문에 setTimeout의 시간이 정확하지 않을 수 있다.
기본적으로 자바스크립트 코드는 동시에 실행될 수 없지만 자바스크립트 상에서 돌아가는 작업이 아닌 입력(input)과 출력(output) 작업은 동시에 실행할 수 있다. 파일 시스템 접근(파일 읽기, 파일 쓰기, 폴더 만들기 등)이나 네트워크를 통한 요청 같은 작업이 I/O의 일종이다. 이러한 작업을 할 때 노드는 논 블로킹 방식으로 처리하는 방법을 제공한다.
논 블로킹이란 이전 작업이 완료될 때까지 대기하지 않고 다음 작업을 수행함을 뜻한다.
노드는 I/O 작업을 백그라운드로 넘겨 동시에 처리한다.
I/O 작업이 없을 경우 논 블로킹 방식으로 코딩을 한다하여도 전체 소요시간이 짧아지는 것은 아니다. 하지만 실행 순서를 바꿔줌으로써 오래 걸리는 작업 때문에 간단한 작업이 대기하는 상황을 막을 수 있다.