1장 노드 시작하기
Node JS ·JS Deep Dive를 통해 자바 스크립트 엔진에 대해 간략하게 공부를 했었다. 이번 NodeJs 교과서를 통해서 중복되는 내용은 skip하고 추가되는 내용에 대해서 정리해 보고자 한다.
1.1 핵심 개념 이해하기
- 노드 공식 사이트(https://nodejs.org/ko/)에서는 노드를 다음과 같이 설명하고 있다.
Node.jsR는 Chrome V8 Javascript 엔진으로 빌드된 자바스크립트 런타임입니다.
- 대부분은 노드를 서버로 사용하는 방법을 익히기 위해 공부할 것이다. 그런데 공식 사이트의 노드 소개 글에는 서버라는 말이 없다. 하지만 걱정하지 말자. 서버라는 말이 없는 이유는 노드가 서버만 실행할 수 있는 것은 아니기 때문이다.
- 먼저 서버와 런타임이 무엇인지 알아 보자.
1.1.1 서버
- 노드를 통해 다양한 자바스크립트 애플리케이션을 실행할 수 있지만, 노드는 서버 애플리케이션을 실행하는 데 제일 많이 사용한다.
- 서버는 네트워크를 통해 클라이언트에 정보나 서비스를 제공하는 컴퓨터 또는 프로그램을 말한다. 클라이 언트란 요청을 보내는 주체로 브라우저 일 수도 있고, 데스크톱 프로그램일 수도 있고, 모바일 앱일 수도 있고, 다른 서버에 요청을 보내는 서버일 수도 있다.
- 노드는 자바스크립트 프로그램이 서버로서 기능하기 위한 도구를 제공하므로 서버 역할을 수행할 수 있다. 왜 다른 언어를 사용하지 않고 굳이 노드를 사용해 서버를 만들까? 이 궁금증을 해결하려면 먼저 노드의 특성을 알아야 한다. 공식 웹 사이트에 게시된 노드 소개 글을 바탕으로 노드의 특성을 알아보자.
1.1.2 자바스크립트 런타임
- 노드는 자바스크립트 런타임이다. 런타임은 특정 언어로 만든 프로그램들을 실행할 수 있는 환경을 뜻한다. 따라서 노드는 자바스크립트 프로그램을 컴퓨터에서 실행할 수 있다. 쉽게 말해 노드는 자바스크립트 실행기라고 봐도 무방하다.
- 기존에는 자바스크립트 프로그램을 웹 브라우저 위에서만 실행할 수 있었다. 브라우저는 자바스크립트 런타임을 내장하고 있으므로 자바스크립트 코드를 실행할 수 있다. 브라우저 외의 환경에서 자바스크립트를 실행하기 위한 여러 시도가 있었으나, 자바스크립트의 실행 속도 문제 때문에 대부분이 큰 호응을 얻지 못했다.
- 하지만 2008년 구글이 V8 엔진을 사용해 크롬을 출시하자 이야기가 달라졌다. 당시 V8 엔진은 다른 자바스크립트 엔진과 달리 매우 빨랐고, 오픈 소스로 코드를 공개했다. 속도 문제가 해결되자 라이언 달(Ryan Dahl)은 2009년 V8 엔진 기반의 노드 프로젝트를 시작했다.
- 노드는 V8과 더불어 libuv라는 라이브러리를 사용한다. libuv 라이브러리는 노드의 특성인 이벤트 기반, 논블로킹 I/O 모델을 구현하고 있습다. 이 모델이 무엇인지, 장단점으로는 어떤 것들이 있는지 이제 알아보자!
1.1.3 이벤트 기반
- 이벤트 기반 시스템에서는 특정 이벤트가 발생할 때 무엇을 할지 미리 등록해둬야 한다. 이를 이벤트 리스너(event listener)에 콜백(callback) 함수를 등록한다고 표현한다.
- 노드도 이벤트 기반 방식으로 동작하므로 이벤트가 발생하면 이벤트 리스너에 등록해둔 콜백 함수를 호출한다. 발생한 이벤트가 없거나 발생했던 이벤트를 다 처리하면, 노드는 다음 이벤트가 발생할 때까지 대기한다.
- 이벤트 기반 모델에서는 이벤트 루프(event loop)라는 개념이 등장한다. 여러 이벤트가 동시에 발생했을 때 어떤 순서로 콜백 함수를 호출할지를 이벤트 루프가 판단한다.
1.1.4 논블로킹 I/O
- 이벤트 루프를 잘 활용하면 오래 걸리는 작업을 효율적으로 처리할 수 있다. 작업에는 두 가지 종류가 있는데, 동시에 실행될 수 있는 작업과 동시에 실행될 수 없는 작업이 있다. 기본적으로 우리가 작성한 자바스크립트 코드는 동시에 실행될 수 없다. 하지만 자바스크립트상에서 돌아가는 것이 아닌 i/o 작업 같은 것은 동시에 처리될 수 있다.
- I/O는 입력(Input)/출력(OutpuO을 의미한다. 파일 시스템 접근(파일 읽기 및 쓰기, 폴더 만들기등)이나 네트워크를 통한 요청 같은 작업이 I/O의 일종이다. 이러한 작업을 할 때 노드는 논블로킹 방식으로 처리하는 방법을 제공한다. 논블로킹(non-blocking)이란 이전 작업이 완료될 때까지 대기하지 않고 다음 작업을 수행하는 것을 의미한다. 반대로 블로킹은 이전 작업이 끝나야만 다음 작업을 수행하는 것을 의미합니다.
- 노드는 I/O 작업을 백그라운드로 넘겨 동시에 처리하곤 한다. 따라서 동시에 처리될 수 있는 작업들은 최대한 묶어서 백그라운드로 넘겨야 시간을 절약할 수 있다.
- 다음 예제는 논블로킹 방식의 코드다.
function longRunningTask() {
console.log('작업 끝');
}
console.log('시작');
setTimeout(longRunningTask, 0);
console.log('다음 작업');
- 결과는 시작 -> 다음 작업 -> 작업 끝이다. setTimeout의 콜백 함수인 longRunningTask가 태스크 큐로 보내지므로 순서대로 실행되지 않는다는 것을 알 수 있다. 다음 작업이 먼저 실행된 후, 오래 걸리는 작업이 완료된다.
- 다만. 아무리 논블로킹 방식으로 코드를 작성하더라도 코드가 전부 우리가 작성한 것이라면 전체 소요 시간이 짧아지지는 않는다. 우리의 코드는 서로 동시에 실행되지 않기 때문이다. 단순히 실행 순서만 바뀔 뿐…
- 하지만 I/O 작업이 없다고 해서 논블로킹이 의미가 없는 것은 아니다. 오래 걸리는 작업을 처리해야 하는 경우, 논블로킹을 통해 실행 순서를 바꿔줌으로써 그 작업 때문에 간단한 작업들이 대기하는 상황을 막을 수 있다는 점에서 의의가 있다. 또한, 논블로킹과 동시가 같은 의미가 아니라는 사실을 알아두자.
1.1.5 싱글스레드
- 스레드를 이해하기 위해서는 프로세스부터 알아야 한다. 프로세스와 스레드의 차이는 다음과 같다.
- 프로세스는 운영체제에서 할당하는 작업의 단위다. 노드나 웹 브라우저 같은 프로그램은 개별적인 프로세스다. 프로세스 간에는 메모리 등의 자원을 공유하지 않는다.
- 스레드는 프로세스 내에서 실행되는 흐름의 단위다. 프로세스는 스레드를 여러 개 생성해 여러 작업을 동시에 처리할 수 있다. 스레드들은 부모 프로세스의 자원을 공유한다. 같은 주소의 메모리에 접근 가능하므로 데이터를 공유할 수 있다.
- 노드가 싱글 스레드라는 말을 들어봤을 것이다. 하지만 엄밀히 말하면 싱글 스레드로 동작하지는 않는다. 노드를 실행하면 먼저 프로세스가 하나 생성된다. 그리고 그 프로세스에서 스레드들을생성하는데, 이때 내부적으로 스레드를 여러 개 생성한다. 그중에서 우리가 직접 제어할 수 있는 스레드는 하나뿐이다. 그래서 흔히 노드가 싱글 스레드라고 여겨지는 것이다.
- 스레드를 작업을 처리하는 일손으로 표현하기도 하는데. 하나의 스레드만 직접 조작할 수 있으므로 일손이 하나인 셈이다. 요청이 많이 들어오면 한 번에 하나씩 요청을 처리한다. 블로킹이심하게 일어나는 작업을 처리하지만 않는다면 스레드 하나로도 충분하다. 블로킹이 발생할 것같은 경우에는 논블로킹 방법으로 대기 시간을 최대한 줄인다.