개요
대표적인 오픈소스 서버 사이드 자바스크립트 플랫폼인 Node.js의 기본 개념과 원리를 이해하고, 이를 활용하여 다양한 웹 애플리케이션을 개발할 수 있도록 핵심 내용을 배운다.
Node.js 란?
Node.js는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임이다. 간단히 말해, 웹 브라우저 밖에서도 JavaScript 코드를 실행할 수 있게 해주는 환경인 셈이다. 기본적으로 JavaScript는 웹 브라우저 안에서만 동작하는 언어였지만, Node.js의 등장으로 JavaScript는 서버 개발, 데스크톱 애플리케이션, 심지어 모바일 앱 개발까지 가능한 전천후 언어로 거듭나게 되었다.
자바스크립트는 1997년부터 웹 개발에 사용되고 있으며 웹 서버 프로그램이나 데이터베이스 연동 등은 주로 자바 언어를 사용해 개발하는 것이 보편적이었다. 그러나 2009년에 Node.js가 처음 등장하면서 자바스크립트가 서버 사이드 프로그래밍에서도 사용될 수 있게 되었다. Node.js는 비동기 이벤트 기반 아키텍처를 채택하여 높은 성능과 확장성을 제공하며, 이를 통해 대규모 네트워크 애플리케이션을 효율적으로 개발할 수 있다.
Node.js의 특징
기본적으로 노드는 자바스크립트이기 때문에 자바스크립트의 언어적 특성을 기본적으로 가지고 있다.
자바 스크립트 기반이라는 의미는 Node.js가 자바스크립트 언어로 작성된 코드를 실행할 수 있는 런타임 환경이라는 것을 의미한다. 이는 개발자들이 이미 익숙한 언어로 서버 사이드 애플리케이션을 개발할 수 있게 해준다는데 큰 의미를 가진다. 특히 프론트엔드 개발자들도 마음만 먹으면 언제든지 서버 사이드 개발에 도전해 볼 수 있는 환경을 제공한다.
자바스크립트 기반의 장점
- 프론트엔드에서 사용하던 자바스크립트를 백엔드에서도 사용 가능
- 하나의 언어로 전체 웹 애플리케이션 개발 가능 (Full-stack)
그 외 주요 특징을 정리하면 다음과 같다.
| 특징 | 설명 |
|---|---|
| 이벤트 기반 | 이벤트 기반 아키텍처를 사용하여 비동기식 프로그래밍을 지원. |
| 논 블로킹 I/O | 논 블로킹 I/O 모델을 사용하여 높은 처리 성능을 제공. |
| 단일 스레드 | 단일 스레드 이벤트 루프를 사용하여 동시성을 처리. |
| 크로스 플랫폼 | Windows, macOS, Linux 등 다양한 운영 체제에서 실행할 수 있다. |
| 풍부한 모듈 | npm(Node Package Manager)을 통해 다양한 모듈과 패키지를 쉽게 설치하고 사용할 수 있다. |
| 커뮤니티 지원 | 활발한 커뮤니티와 풍부한 자료를 통해 개발자들에게 많은 지원을 제공한다. |
서버 프로그램은 필수적으로 동시에 여러 클라이언트의 요청을 처리해야 한다. 따라서 서버 프로그램은 동시성(Concurrency)과 병렬성(Parallelism)을 효과적으로 지원해야 성능적으로 요구되는 품질속성을 충족시킬수 있다.
이러한 중요한 요소를 개발자가 직접 해결해야 하는 구조는 위험성이 크기 때문에 프레임워크 레벨에서 이를 지원하는 것이 바람직하다. Node.js는 비동기 기반 I/O를 기본으로 하여, 이벤트 기반 아키텍처를 통해 높은 동시성을 지원한다.
모든 프로그래밍 언어에서 입출력(I/O) 작업은 시간이 소요될 수 있고 중간에 문제가 발생할 수 있다. 따라서 안정적인 입출력을 위해 동기화 모델을 기본으로 사용한다.
스레드 모델
스레드는 운영체제에서 프로세스 내에서 실행되는 독립적인 실행 단위로, 멀티스레드 프로그래밍은 여러 스레드가 동시에 실행되어 작업을 병렬로 처리하는 방식이다.
일반적으로 싱글스레드의 경우 하나의 작업이 완료될 때까지 다른 작업이 대기해야 하므로, 동시에 여러 요청을 처리하는 데 한계가 있다. 반면 멀티스레드는 여러 스레드가 동시에 실행되어 여러 작업을 병렬로 처리할 수 있어 동시성이 높아진다.

멀티 스레드 문제점
앞에서 언급한것 처럼 프로그램에서 다중 요청을 처리하기 위해서는 기본적으로 멀티스레드 방식을 사용할 수 있다. 그러나 멀티 스레드는 다음과 같은 문제점들이 있다.
- 스레드 간의 동기화 문제
- 스레드 비대화로 인한 리소스 낭비 문제
- 성능 저하나 코드 복잡성으로 인한 문제 발생 가능
Node.js는 싱글 스레드 모델을 사용하지만, 이벤트 기반 논블로킹 I/O와 이벤트 루프를 통해 높은 동시성을 지원한다. 이를 통해 멀티스레드 방식의 복잡성을 피하면서도 효율적으로 다중 요청을 처리할 수 있다.
Node.js 아키텍처
이벤트 기반 논블로팅 I/O는 Node.js의 핵심 개념 중 하나로, 이를 통해 높은 동시성과 성능을 제공한다. 이해를 위해 먼저 논블로킹 I/O의 개념을 이해해야 한다. 일반적인 I/O는 동기 모델로 입출력 작업이 완료될때 까지 다음 작업을 진행하지 않는 방식을 말한다. 반면 논블로킹 I/O는 비동기 모델로 입출력 작업이 완료될 때까지 기다리지 않고, 다른 작업을 계속 진행할 수 있는 방식을 의미한다.
논블로킹 (Non-blocking)
전통적인 방식은 파일 읽기나 데이터베이스 조회와 같은 I/O 작업이 완료될 때까지 다음 코드를 실행하지 않고 기다리는 것으로 이를 블로킹(Blocking) 방식이라고 한다. 반면, 논블로킹은 I/O 작업이 시작되면 결과를 기다리지 않고 즉시 다음 코드를 실행한다. I/O 작업은 백그라운드에서 진행되고, 완료되면 미리 정해둔 콜백 함수를 호출하여 결과를 처리하는 형태가 된다. 이는 마치 식당에서 주문을 받은 요리사가 요리가 완성될 때까지 손님 옆에 서서 기다리는 것이 아니라, 주방으로 돌아가 다른 손님의 주문을 처리하는 것과 비슷하다.
이벤트 기반 (Event-driven)
이벤트란 프로그램에 영향을 미치는 특정 상황을 말하며 논블로팅 I/O에서는 모든 작업이 이벤트를 중심으로 이루어진다. I/O 작업이 완료되거나, 타이머가 만료되거나, 사용자의 클릭이 발생하는 것 등이 모두 이벤트이다. 이러한 이벤트가 발생하면 미리 등록해둔 콜백 함수가 실행되어 처리되는 방식이다.
이벤트 루프 (Event Loop)
이벤트 루프는 이 모든 것을 조율하는 핵심 메커니즘으로 들어오는 요청을 지속적으로 모니터링하고 콜백 함수를 실행시키는 순환 고리이다. 이벤트 루프는 단일 스레드에서 동작하지만, 내부적으로는 여러 스레드를 활용하여 I/O 작업을 처리한다. 예를 들어, 파일 시스템 접근이나 네트워크 요청과 같은 작업은 운영 체제의 스레드 풀에서 처리되고, 완료되면 이벤트 루프가 이를 감지하여 콜백 함수를 실행한다.

- V8 JavaScript Engine: Node.js는 구글의 V8 자바스크립트 엔진을 사용하여 자바스크립트 코드를 빠르게 실행한다. V8은 자바스크립트 코드를 네이티브 머신 코드로 컴파일하여 높은 성능을 제공한다.
- Libuv: Libuv는 Node.js의 비동기 I/O를 지원하는 라이브러리로, 이벤트 루프와 스레드 풀을 관리한다. 이를 통해 다양한 운영 체제에서 일관된 비동기 I/O 동작을 제공한다.
- Event Queue: 이벤트 큐는 완료된 I/O 작업의 콜백 함수들이 대기하는 곳이다. 이벤트 루프는 이 큐를 지속적으로 확인하여 대기 중인 콜백 함수를 실행한다.
- Worker Threads: JS의 단일 스레드 한계를 극복하기 위해 도입된 기능으로 CPU 집약적인 작업을 메인 스레드로 부터 분리해 별도의 스레드에서 처리할 수 있게 해주는 메커니즘이다. Node.js는 내부적으로 libuv 라이브러리를 사용하여 백그라운드 스레드 풀을 관리하기 때문에 메인 스레드를 블로킹 하지 않는다. 하지만 CPU 집약적인 작업은 메인스레드(이벤트루프)를 블로킹 할 수 있기 때문에 worker threads를 사용해 별도의 스레드에서 처리할 수 있게 해준다.
Node.js의 스레드 모델 요약
- Node.js는 자바스크립트 코드의 실행에 있어서 싱글 스레드로 동작.
- I/O 작업(파일 시스템 접근, 네트워크 통신 등)과 같은 비동기 처리는 libuv 라이브러리를 통한 백그라운드 내부 스레드 풀에서 처리.
- CPU 집약적인 작업은 별도의 스레드(Worker Threads)에서 처리할 수 있도록 지원 하므로 메인 스레드의 블로킹을 방지.
- 표면적으로는 싱글 스레드처럼 보이지만, 실제로는 멀티 스레드의 도움을 받아 높은 동시성을 확보하며 고성능을 발휘.
생각해 봅시다
- Node.js를 사용하는 장점은?
- 단일 스레드 모델과 멀티 스레드 모델을 실제 사례를 들어 설명해 보자.
- 동기 모델과 비동기 모델의 차이점을 설명해 보자.
- Node.js 에서의 이벤트 루프란?