on1ystar 머릿속을 정리, 기록하는 곳

Node.js 란?, 특징-이벤트 루프/논 블로킹/싱글 스레드


많은 사람들이 JavaScript를 배우고, 매력있어 하는 이유는 다양하겠지만 가장 대중적인 이유 중 하나는 JavaScript 하나로 웹 개발의 Front-end, Back-end부터 AndroidIOS 앱을 만들 수 있는 하이브리드 앱 개발까지 가능하기 때문이다.

특히나 Front-end 영역에서 HTML과 CSS를 편리하게 제어할 수 있는, 동적인 프로그래밍이 가능한 언어는 JavaScript가 유일하기 때문에 개발자라면 갖춰야 할 최소한의 소양에 포함된다고 해도 무방하다.

때문에 개발자의 길을 가야겠다고 마음 먹은 사람들 중 다수가 어차피 배워야 하고, 다양한 영역에서 사용할 수 있는 JavaScript를 주력 언어로 익히기 위해 노력한다.

실제로 아래 링크들은 유명한 Programming language ranking 사이트들의 최신 랭킹인데, JavaScript가 상위권에 위치하고 있으며 주목할 점은 작년보다 향상 중이라는 것이다.

https://octoverse.github.com/

https://www.tiobe.com/tiobe-index/

https://pypl.github.io/PYPL.html

이러한 긍정적인 지표들을 만든 핵심 역할을 한 것이 Node.js의 등장이다. Ryan Dahl에 의해 최초로 개발되었고, Inangural JSConf EU conference에서 세계에 알려졌다. 덕분에 브라우저에서만 동작하던 JavaScript가 브라우저 밖 Node 환경에서도 동작할 수 있게 됐다.

웹 브라우저(chrome)

windows 터미널에서 node 실행

즉, python이나 java같은 프로그래밍 언어처럼 서버 사이드에서 동작하는 언어로 쓰일 수 있게 됐다.

공식 사이트에서 소개하는 Node.js는 다음과 같다.

Node.js®는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임입니다. (Node.js 공식 사이트)

여기서 뜻하는 런타임이란 특정 언어로 만든 프로그램들을 실행할 수 있는 환경을 뜻한다.

즉, Node.js는 JavaScript 프로그램을 컴퓨터에서 실행할 수 있게 해주는 도구나 환경같은 것이다.

2008년 전만 해도 자바스크립트는 속도 문제 때문에 웹 브라우저에 내장되어 있는 자바스크립트 런타임 위에서만 실행될 수 있었다. 하지만 구글이 Chrome V8 JavaScript 엔진을 출시했는데, 다른 자바스크립트 엔진보다 월등히 빠랐고 오픈 소스로 공개한 덕에 2009년 부터 자바스크립트를 웹 브라우저 밖에서도 실행할 수 있는 노드 프로젝트를 시작할 수 있었다.


이벤트 루프


브라우저에서 사용하는 자바스크립트처럼 이벤트 리스너에 콜백 함수를 등록할 수 있다.

이벤트 루프는 가능하다면 언제나 시스템 커널에 작업을 떠넘겨서 Node.js가 논 블로킹 I/O 작업을 수행하도록 해준다. Node.js를 시작할 때 이벤트 루프를 초기화하고 제공된 입력 스크립트를 처리한다.

이벤트 루프는 여러 개의 큐로 구성되어 있다.

자세한 설명 참고: https://nodejs.org/ko/docs/guides/event-loop-timers-and-nexttick/

아래는 이벤트 루프 처리를 시각적으로 볼 수 있는 좋은 사이트다.

http://latentflip.com/loupe

이벤트 루프(주황색 화살표): 이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백 함수의 실행 순서를 결정하는 역할. 노드가 종료될 때까지 이벤트 처리를 위한 작업을 반복

Web Apis(백그라운드) : setTimeout()과 같은 타이머나 이벤트 리스너들이 대기하는 곳으로 여러 작업이 동시에 실행될 수 있음.

Callback Queue(태스크 큐): 이벤트 발생 후, 백그라운드에서는 태스크 큐로 타이머나 이벤트 리스너의 콜백 함수를 보내면 보통 큐에 들어온 순서대로 처리(특정한 경우에는 순서가 바뀌기도 함.

위 사이트에서 다양한 코드들을 실험해 보면 이벤트 루프에 대한 이해에 큰 도움이 된다.

  1. 예를 들어 타이머를 setTimeout()으로 3초 뒤에 run()이라는 함수가 실행되도록 했다.
  2. Call Stack에서 setTimeout()이 호출되면 백그라운드로 run()을 보낸 뒤, 3초간 대기한다.
  3. 3초 뒤 백그라운드에서 태스크 큐로 run() 함수가 보내진다.
  4. Call Stack이 모두 비워지면 이벤트 루프가 태스크 큐의 콜백들을 하나 씩 Call Stack으로 올린다.

단, 이때 만약 호출 스택이 비워지지 않고 차있으면 run()함수는 3초가 지난 후지만 실행되지 않고 대기해야 한다.


논 블로킹 비 동기 I/O


이벤트 루프를 통해 동시에 처리할 수 있는 작업들을 백그라운드로 보내 비동기 방식으로 처리하면 효율을 매우 높일 수 있다.

일반적으로 우리가 작성한 코드는 동시에 실행될 수 없지만, I/O 작업들은 노드가 논 블로킹(non-blocking)으로 동시에 처리할 수 있으므로 최대한 묶어서 백그라운드로 넘기는 것이 좋다.

하지만 논 블로킹 == 동시는 아니다. 동시 처리가 가능한 작업들(I/O 작업들)을 논 블로킹 처리를 해야 동시성에 처리하는 비 동기 효과를 얻을 수 있다.

이런 이유 때문에 I/O 작업이 많은 서버로 적합하다.


싱글 스레드


정확하게 말하면 node는 싱글스레드가 아니다. node도 여러개의 스레드를 가지고 있다. 그러나 자바스크립트를 실행하는 스레드는 단 하나이므로 node를 싱글스레드 라고 한다. 여기서 싱글스레드가 바로 이벤트루프다.

싱글 스레드를 사용하면 복잡한 멀티 스레드 방식으로 프로그래밍 하지 않아도 되어 생상성이 높아지며, 위에서 설명한 논 블로킹 비 동기 방식으로 효율도 나쁘지 않다. 특히 I/O 작업은 멀티 스레딩보다 멀티 브로세싱 방식이 더 효율적이다.

하지만 CPU 연산이 많은 데이터 사이언스 등에는 스레드 하나로 처리하기 버거운 면이 있으며, 만약 어떠한 에러로 스레드 하나가 멈추면 전체 서버가 멈추게 되므로 주의해야 한다.


Reference

Node.js 교과서 - 조현영

Node.js 공식 사이트

https://nodejs.dev/learn