Node.js를 파보자

nodejs가 무엇인가요? 어디에 사용할 수 있나요?

Node.js는 Javascript를 브라우저 밖에서도 실행할 수 있도록 하는 비동기 이벤트 기반의 Javascript 런타임으로서, 확장성 있는 네트워크 애플리케이션을 만들 수 있도록 설계되어 있습니다.


싱글스레드 vs. 멀티스레드

싱글스레드는 프로세스 내에서 하나의 스레드가 하나의 요청만을 수행합니다. 해당 요청이 수행될 때 다른 요청을 함께 수행할 수 없고, 진행되고 있는 요청이 예정되어 있는 요청을 블로킹하기 때문에 이를 싱글스레드 블로킹 모델이라고 합니다.

멀티스레드는 스레드 풀에서 실행의 요청만큼 스레드를 매칭하여 작업을 수행합니다. 스레드 풀에 스레드가 늘어날수록 CPU 비용을 소모하고, 요청이 적다면 사용하지 않는 스레드가 생긴다는 효율성 측면의 단점을 가지고 있습니다.


왜 nodejs 는 single-threaded 인가요?

Node.js는 하나의 스레드로 동작하지만, 비동기 I/O 작업을 통해 요청들을 블로킹하지 않는, 싱글스레드 논블로킹 모델입니다. 동시에 많은 요청들을 비동기로 수행하기 때문에, 싱글스레드일지라도 논블로킹이 가능한 것입니다.

또한 Node.js는 클러스터링을 통해 프로세스를 포크하여 멀티스레드인 것처럼 사용될 수 있습니다. 이는 트래픽에 따라 서버의 확장성이 용이하다는 장점을 갖습니다.


nodejs의 클러스터(cluster)란?

Node.js는 싱글스레드이지만, cluster 모듈을 사용하면 작업을 병렬적으로 처리하도록 하여 손쉽게 멀티 스레드로 구현이 가능합니다. 이를 이용하여 요청을 분산시켜서 서버가 오버로드 되지 않도록 하는 것입니다. 이는 성능 개선에도 큰 효과를 줍니다.

Node.js는 기본적으로 v8엔진의 제한을 그대로 반영하기 때문에 하나의 프로세스가 사용할 수 있는 메모리의 양이 제한되어 있습니다.

cluster 모듈은 노드가 CPU 코어를 모두 사용할 수 있게 해주는 모듈로서, 기본적으로 클러스터를 사용하면 포트를 공유하는 프로세스들을 생성하게 되고, 포트를 공유하는 노드 프로세스를 여러 개 두어 요청이 많이 들어올 시에 병렬로 실행된 서버의 개수만큼 요청이 분산되게 할 수 있습니다. 따라서 서버 부담을 줄일 수 있습니다.


클러스터는 어떻게 동작하나?

클러스터에는 마스터 프로세스와 워커 프로세스가 있습니다. 마스터 프로세스는 CPU 코어 수 만큼 워커 프로세스를 만들고 8080 포트에서 대기합니다. 8080 포트로 요청이 들어오면, 마스터 프로세스는 워커 프로세스에게 요청을 분배합니다. 워커 프로세스는 실질적으로 일을 하는 프로세스를 말합니다.


CPU 코어를 모두 사용한다는 것의 의미?

코어가 8개인 서버가 있을 경우 노드는 기본적으로 하나의 코어만 활용합니다. 그러나 cluster 모듈을 사용하면 코어 하나 당 노드 프로세스 하나가 돌아가게 할 수 있습니다. 성능이 8배가 되는 것은 아니지만 코어 하나만 사용할 때보다 훨씬 개선되는 것은 사실입니다.

그러나 코어 하나 당 프로세스가 돌아가기 때문에 메모리 세션을 공유하지 못하는데, 이는 Redis 서버를 도입하여 해결할 수 있습니다.


이벤트 기반(Event-driven)

이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식이며, Node.js는 이벤트 리스너에 등록해둔 콜백함수를 실행하는 방식으로 동작합니다.


이벤트 루프

이벤트 루프는 이벤트에 따라 호출되는 콜백함수를 관리하는 것입니다.

이벤트 루프는 여러 개의 페이즈들을 갖고 있으며, 해당 페이즈들은 각자 큐를 갖고 관리합니다. 노드 프로세스가 종료될 때까지 일정 규칙에 따라 여러개의 페이즈들을 라운드 로빈 방식으로 계속 순회합니다. 페이즈에 의해 관리되는 큐들은 FIFO(선입선출) 순서로 콜백함수들을 처리합니다.


라운드 로빈 프로세스 스케줄링

대개 리스트의 맨 위에서 아래로 가며 하나씩 뽑고, 끝나면 다시 맨 위로 돌아가는 방식으로 진행되는 뽑기 방식. 컴퓨터 운영에서, 프로그램 프로세스들에게 컴퓨터 자원을 사용할 수 있는 기회를 공정하게 부여하기 위한 하나의 방법으로, 각 프로세스에 일정 시간을 할당하고, 그 시간이 지나면 그 프로세스는 잠시 보류한 뒤 다른 프로세스에게 기회를 주는 식으로 돌아가며 기회를 부여하는 운영방식이다.


Node.js의 내부 구조

Node.js는 크게 내장 라이브러리와 v8엔진, 그리고 libuv로 구성되어 있습니다. libuv는 Node.js에 이벤트루프를 제공하는 라이브러리입니다.


nodejs에서의 논블로킹 I/O 모델

Inputrhk Output이 관련된 작업, 예를 들어 데이타베이스 CRUD, third pary api, 파일 시스템 등의 블로킹 작업들을 OS 커널 혹은 libuv의 스레드 풀 같은 백그라운드에서 수행하고, 이를 비동기 콜백함수로 이벤트 루프에 전달하는 모델.

I/O들은 OS 커널 혹은 libuv 내의 스레드 풀에서 담당합니다. libuv는 OS 커널에서 어떤 비동기 작업들을 지원해주는지 알고 있기 때문에, 해당 작업에 대한 OS 커널의 비동기 지원 여부에 따라 커널이나 스레드 풀로 분기합니다. 커널이나 스레드 풀에서 작업 수행 완료 시 이벤트 루프에 이를 알려주어 이벤트 루프에 콜백함수로 등록됩니다.

(libuv에서 스레드 풀은 멀티스레드로 이루어져 있기 때문에 nodejs는 완전한 싱글 스레드가 아닙니다.)


nodejs에서 event loop란 무엇인가요?

Node.js가 시작되면 스레드가 생기고, 이벤트 루프가 생성됩니다. 이벤트 루프는 6개의 페이즈를 라운드 로빈 방식으로 순회하며 동작합니다.

  1. timers 단계는 루프의 시작을 의미하며 setTimeout()과 setInterval() 같은 timer 함수들이 처리됩니다. 이 때 타이머에 등록된 콜백을 언제 실행할지만 관리하고, 콜뱀함수의 실행은 poll 단계의 가장 앞부분에서 이루어집니다.

  2. IO callbacks, pending callbacks라고도 불립니다. 이 단계에서는 다음 페이즈를 실행하기 위해 연기된 콜백을 실행합니다. 각 페이즈는 모든 작업을 다 실행하지 않고 일정량만 실행 후 다음 페이즈로 넘어가기 때문에, 이 페이즈에서 이전에 처리하지 못한 작업을 실행합니다. 또한 TCP 오류 같은 시스템 작업의 콜백을 실행합니다.

  3. idle,과 prepare phase는 매 틱마다 실행되며, nodejs의 내부 관리를 위해 사용됩니다.

  4. poll 단계에서는 I/O와 연관된 콜백을 실행합니다. 먼저 timers 단계에서 스케줄링 되었던 콜백들 중에 시간이 지난 타이머의 콜백을 실행하고, poll 단계의 큐에 있는 이벤트들을 처리합니다. poll 큐에 쌓인 콜백 함수들을 한도가 넘지 않을 때까지 모두 동기적으로 실행하며, 한도가 넘어가 더이상 실행할 콜백함수가 없을 때는 별도의 규칙을 따라 다음 단계로 넘어가거나 대기합니다.

  5. check 단계에서는 setImmediate()를 통해 등록된 모든 콜백을 실행하는데, poll 단계에서는 이 check 단계를 검사하여 setImmediate()가 있는지 확인하고, 있을 경우 check 단계로 넘어가며, 없을 경우 timer 단계에 실행할 timer 함수가 있는지 확인합니다. timer 함수를 실행할 수 있는 시간까지 대기한 후 timer단계로 넘어가고, 대기하는 동안에 poll 큐에 콜백함수가 등록되면 즉시 실행합니다.

  6. close 단계에서는 socket.on(‘close’, …)와 같은 close, destroy 이벤트 타입에 대한 모든 콜백이 실행됩니다.


다른 개발환경이 아닌 nodejs 를 사용해야 하는 장점이 무엇인가요?

단일 쓰레드 이벤트 루프 기반 비동기 방식으로, 하나의 쓰레드가 request를 받으면 다음 처리에 요청을 보내놓고 다른 작업을 처리하다가 먼저 요청한 작업이 끝나면 이벤트를 받아 응답을 보냅니다. 동시 request가 오더라도 처리가 완료될 때까지 기다리지 않아도 되기 때문에 서버 부하가 적습니다. 쓰레드 기반 동기 방식의 경우 동시에 많은 request가 들어올 때 많은 쓰레드가 필요하게 되어 서버 과부하가 발생합니다.

npm을 통한 다양한 모듈을 제공하기 때문에 npm을 이용해 필요한 라이브러리와 패키지를 검색해 설치하고 사용할 수 있기 때문에 개발 속도와 효율성이 크게 향상합니다.

자바스크립트를 사용하기 때문에 새로운 언어를 습득하지 않고도 서버 기술을 개발하고 응용할 수 있습니다.

빠른 응답시간과, 빠른 개발이 필요할 경우, 혹은 네트워크 스트리밍 서비스나 채팅 서비스 같은, 데이터를 실시간으로 다루는 애플리케이션이나 싱글페이지 애플리케이션, 입출력이 잦은 애플리케이션 등 비동기 방식에 어울리는 서비스를 개발할 때 node.js를 사용하는 것이 효율적입니다.

Categories:

Node.js

Load Comments