런타임 : 특정 언어로 만든 프로그램들을 실행할 수 있게 해주는 가상 머신(크롬의 V8 엔진 사용)의 상태Node.js®는 크롬 V8 자바스크립트 엔진으로 빌드된 자바스크립트 런타임이다.
노드는 서버의 역할도 수행할 수 있는 자바스크립트 런타임으로써, 자바스크립트로 작성된 서버를 실행할 수 있다. (서버 실행을 위해 필요한 http/https/http2 모듈을 제공)
노드는 V8과 더불어 libuv라는 라이브러리를 사용한다.
V8과 libuv는 C와 C++로 구현되어 있다.
libuv 라이브러리는 노드의 특성인 이벤트 기반, 논블로킹 I/O 모델을 구현하고 있다.
이벤트 기반(event-driven)이란 이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식을 의미한다.
이벤트로는 클릭이나 네트워크 요청 등이 있을 수 있다.
이벤트 기반 시스템에서는 특정 이벤트가 발생할 때 무엇을 할지 미리 등록해둬야 하는데, 이를 이벤트 리스너(event listener)에 콜백(callback) 함수를 등록한다고 표현한다.
노드도 이벤트 기반 방식으로 동작하므로, 이벤트가 발생하면 이벤트 리스너에 등록해둔 콜백 함수를 호출한다. 발생한 이벤트가 없거나 발생했던 이벤트를 다 처리하면, 노드는 다음 이벤트가 발생할 때까지 대기한다.
이벤트 기반 모델에서 이벤트 루프(event loop)라는 개념이 등장하는데, 여러 이벤트가 동시에 발생했을 때 어떤 순서로 콜백 함수를 호출할지를 이벤트 루프가 판단한다. 코드가 실행되는 내부 과정을 묘사한 그림은 아래와 같다.
setTimeout 같은 타이머나 이벤트 리스너들이 대기하는 곳이다. 여러 작업이 동시에 실행될 수 있고, 작업이 완료되는 순간 태스크 큐로 작업을 보낸다.이벤트 루프를 활용하면 오래 걸리는 작업을 효율적으로 처리할 수 있다.
특히 파일 시스템 접근이나 네트워크 요청 같은 I/O 작업은 자바스크립트 엔진이 직접 처리하지 않으므로 동시에 처리될 수 있는데, 이를 논블로킹 I/O라고 부른다.
이벤트 기반, 논블로킹 모델과 더불어 노드의 특징으로는 싱글 스레드가 있다. 싱글 스레드란 스레드가 하나뿐이라는 것을 의미하는 것으로, 작성된 자바스크립트 코드가 동시에 실행될 수 없는 이유이기도 하다. 이후 있을 설명을 위해 프로세스와 스레드에 대해 간단하게 설명하고 넘어가겠다.
노드는 엄밀히 말하면 싱글 스레드로 동작하지는 않는다.
노드를 실행하면 먼저 프로세스가 하나 생성된다. 그리고 그 프로세스에서 스레드들을 생성하는데, 이때 내부적으로 스레드를 여러 개 생성한다. 그 중에서 직접 제어할 수 있는 스레드는 하나뿐이다. 그래서 흔히 노드가 싱글 스레드라고 여겨지는 것이다.
노드는 하나의 스레드만 직접 조작할 수 있으므로 요청이 많이 들어오면 한 번에 하나씩 요청을 처리한다.
블로킹이 심하게 일어나는 작엄을 처리하지 않는다면 스레드 하나로 충분하지만, 블로킹이 발생할 것 같은 경우에는 논블로킹 방법으로 대기 시간을 줄인다.
먼저 싱글 스레드 + 블로킹은 다음과 같다.
음식점에서 점원이 한 명 있고, 손님은 여러 명이 있다. 점원은 주문을 받아 주방에 넘기고, 주방에서 요리가 나오면 손님에게 서빙을 한다. 이런 구조라면 다음 손님은 이전 손님의 요리가 나올 때까지 기다리고 있어야 한다. 이것이 싱글 스레드 + 블로킹 모델이다.
싱글 스레드 + 논블로킹은 다음과 같다.
이번에는 점원이 한 손님의 주문을 받고, 주방에 주문 내역을 넘긴 뒤 다음 손님의 주문을 받는다. 요리가 끝나기까지 기다리는 대신, 주문이 들어왔다는 것만 주방에 계속 알려준다. 주방에서 요리가 완료되면 완료된 순서대로 손님에게 서빙한다. 점원의 주문 처리 방식에 따라 완료되는 순서가 다를 수 있으므로, 주문이 들어온 순서와 서빙하는 순서가 일치하지 않을 수도 있다.
노드는 기본적으로 싱글 스레드, 논블로킹 모델을 사용하므로 노드 서버 또한 동일한 모델일 수밖에 없다.
서버에는 기본적으로 I/O 요청이 많이 발생하므로, I/O 처리를 잘하는 노드를 서버로 사용하면 좋다. 특히 AI 시대에 접어들면서 ChatGPT나 Claude 같은 대규모 언어 모델(LLM)을 활용한 서비스를 만든다고 가정한다면, 사용자가 질문하고 답변을 받기까지는 오랜 시간이 걸린다. 이를 '지연 시간이 긴 I/O 작업'이라고 부른다.
노드는 libuv 라이브러리를 사용해 이러한 대기 시간을 논블로킹 방식으로 처리한다. 따라서 스레드 하나가 수천, 수만 명의 사용자 요청을 받아 AI 모델로 보내고, 답변이 올 때까지 다른 작업을 처리할 수 있다.
또한 현대 웹 아키텍처의 핵심인 MSA(마이크로서비스 아키텍처)와 BFF(Backend for Frontend)패턴에서도 사실상 표준으로 자리 잡고 있다.
BFF : 프론트엔드 요구사항에 맞게 여러 백엔드 API를 조합(Aggregation)하고 가공하는 중간 서버이다. 노드의 논블로킹 I/O는 여러 마이크로서비스로 동시에 네트워크 요청을 보내고 효율적으로 취합하는 데 최적의 성능을 발휘할 수 있다. 또한 프론트엔드와 동일한 언어를 사용하므로 생산성과 유지보수 측면에서 우위를 가진다.
MSA : 서비스를 작고 독립적인 단위로 쪼개는 구조이다. 노드는 Spring Boot 같은 무거운 프레임워크에 비해 가볍고 시작(Cold Start) 속도가 빨라, 컨테이너 기반의 클라우드 네이티브 환경에 매우 적합하다. 서비스 간에 수많은 네트워크 통신(HTTP, gRPC 등)이 발생하는 MSA 환경에서 노드의 비동기 처리는 유리하다.
하지만 노드는 여전히 CPU 부하가 큰 작업에는 적합하지 않다. 작성된 코드는 모두 스레드 하나에서 처리되기 때문에, 이미지 리사이징이나 복잡한 수학 연산처럼 CPU를 계속 쓰는 작업을 직접 처리하면, 그동안 스레드가 멈춰서 다른 요청을 처리하지 못한다.
AI 서비스 개발에서 무거운 연산(모델 학습 및 추론)은 주로 파이썬(Python) 기반의 GPU 서버나 외부 API가 담당한다. 노드는 이들과 통신하며 결과를 사용자에게 전달하는 오케스트레이션 역할을 주로 수행하므로, CPU 단점은 크게 문제되지 않는다.
(굳이 노드 자체에서 CPU 작업을 한다면, AWS 람다(AWS Lambda)나 구글 클라우드 펑션스(Google Cloud Functions) 같은 서버리스 서비스로 작업을 분리하거나, 노드 12 버전부터 안정화된 워커 스레드(Worker Threads)를 사용할 수 있다.)
| 장점 | 단점 |
|---|---|
| 멀티 스레드 방식에 비해 적은 컴퓨터 자원 사용 | 기본적으로 싱글 스레드라서 CPU 코어를 하나만 사용 |
| AI API 호출 등 I/O 작업이 많은 서버로 적합 | 수학 연산, 이미지 처리 등 CPU 사용량이 큰 작업을 많이 하는 서버로는 부적합 |
| 멀티 스레드 방식보다 쉬운 비동기 프로그래밍 | 하나뿐인 스레드가 멈추지 않도록 관리 필요 |
| 웹 서버가 내장되어 있음 | 서버 규모가 커졌을 때 고도화된 아키텍처 필요 |
| 자바스크립트(타입스크립트)를 사용해 프론트엔드와 통합 용이 | C/C++/Go/Rust 등에 비해 자체 연산 속도는 느림 |
| JSON 형식과 호환이 쉬움 |
처음에 노드는 대부분 서버로 사용했지만, 노드는 자바스크립트 런타임이므로 용도가 서버에만 한정되지 않는다.
노드 기반으로 돌아가는 대표적인 웹 프레임워크로는 React, Vue, Angular, Svelte 등이 있다.
엄밀히 말하면 이들은 브라우저에서 실행되지만, 개발 환경 구축과 빌드 과정, 그리고 서버 사이드 렌더링(Next.js, Nuxt 등)을 위해 노드가 필수적으로 사용된다.