Node.js 란?
Javascript를 브라우저 밖에서도 실행할 수 있도록 하는 Javascript의 런타임을 말한다.
런타임: 특정 언어로 만든 프로그램을 실행할 수 있는 환경.
아래는 Node.js 공식 사이트에 게시된 Node.js의 설명 글이다.
Node.js®에 대해서
비동기 이벤트 주도 JavaScript 런타임으로써 Node.js 는 확장성 있는 네트워크 애플리케이션을 만들 수 있도록 설계되었습니다.
비동기(Asynchronous), 이벤트 주도 (Event-driven), Non-Blocking I/O, 확장성 등의 키워드들을 설명하기에 앞서, 프로세스(Process)와 스레드(Thread)에 대한 개념을 간단히 살펴보겠다. 프로세스와 스레드는 Node.js의 동작원리를 이해함에 있어 중요한 개념이다.
프로세스: 메모리에 올라와 실행되고 있는 프로그램의 인스턴스. 실행되고 있는 프로그램(독립적인 개체).
스레드: 프로세스 내에서 할당받은 실행의 단위. 스레드는 프로세스 당 CPU의 코어 개수만큼 생성될 수 있습니다.
* 스레드는 프로세스 내의 메모리 공간을 공유하지만, 각각의 프로세스는 별도의 메모리 공간을 갖습니다.
Node.js 내부 구조와 동작 방식
Node의 가장 큰 특징은 “싱글 스레드 논 블로킹”이기 때문에, 하나의 스레드로 동작 하지만 I/O 작업이 발생한 경우 비동기적으로 처리할 수 있다.
(또한 Node.js는 클러스터링을 통해 프로세스를 포크(fork)하여 멀티스레드인것 처럼 사용될 수 있다. 트래픽에 따라서 프로세스를 포크할 수 있으므로 서버의 확장성이 용이하다는 장점을 갖는다.)
하나의 스레드로 동작을 하면, 만약에 파일 읽기 작업을 한다고 하면 파일을 읽고 다 읽을때까지 아무것도 실행하지 않고 기다렸다가 완료되면 다른 명령들이 실행되는데 노드는 그렇지 않고 하나의 스레드 만으로 여러 비동기 작업을 실행할 수 있다.
그 이유가 무엇일까?
Node.js 구조

Node.js는 C++로 작성된 런타임이고 그 내부에 V8 Engine을 가지고 있다. 그 덕분에 웹이 아닌 로컬에서도 자바스크립트를 실행할 수 있다.
외에도 내부에는 libuv라는 라이브러리가 존재한다.
libuv
C++로 작성된 Node가 사용하는 비동기 I/O 라이브러리
운영체제의 커널을 추상화한 Wrapping 라이브러리 : 커널이 어떤 비동기 API를 지원하는지 알고있다.
* 커널이란?
운영체제 중 항상 메모리에 올라가 있는 운영체제의 핵심 부분으로써 하드웨어와 응용 프로그램 사이에서 인터페이스를 제공하는 역할을 하며 컴퓨터 자원들을 관리하는 역할을 한다. 즉, 커널은 인터페이스로써 응용 프로그램 수행에 필요한 여러가지 서비스를 제공하고, 여러가지 하드웨어(CPU, 메모리) 등의 리소스를 관리하는 역할

- libuv는 I/O 처리가 발생하면 커널이 지원하는지 알아보고
- 지원한다면 커널에게 비동기적으로 대신 요청했다가
- 응답이 오면 그 응답을 우리에게 전달해준다.
- 만약 지원을 하지 않는다면 자신만의 워커 스레드가 담긴 스레드 풀을 사용한다.
스레드 풀

libuv는 기본적으로 4개의 스레드를 가지는 스레드 풀을 생성한다. 우리가 요청한 작업을 커널이 지원하지 않으면 이 스레드풀을 생성해서 작업을 맡긴다.
스레드가 작업을 완료하면 libuv에게 작업 완료했다고 알려주고, libuv가 우리에게 완료된 작업을 응답해준다.
요약
- libuv는 운영체제의 커널을 추상화해서 비동기 API를 지원한다.
- libuv는 커널이 어떤 비동기 API를 지원하고 있는지 알고 있다.
- 만약 커널이 지원하는 비동기 작업을 libuv에게 요청하면 libuv는 대신 커널에게 이 작업을 비동기적으로 요청해준다.
- 만약 커널이 지원하지 않는 비동기 작업을 libuv에게 요청하면 livuv는 내부에 가지고있는 스레드 풀에게 이 작업을 요청해준다.
Node.js 의 싱글 스레드 논 블로킹
위와 같이 Node의 구조를 보면, Node는 I/O 작업을 자신의 메인 스레드가 아닌 다른 스레드에 위임 함으로써 싱글 스레드로 동작하지만 비동기 작업을 지원할 수 있다.
libuv한테 일을 대신 시켜서 비동기 작업을 할 수 있는 것이다. 그런데 이렇게 비동기 작업을 할 수 있는기반엔 이벤트 루프가 존재한다.

이벤트 기반(Event-driven)
이벤트 기반이란 이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식을 의미한다. Node.js는 이벤트 리스너에 등록해둔 콜백함수를 실행하는 방식으로 동작한다.
router.get('/', (req,res,next)=> {
// router.get 이벤트에 대한 콜백함수 로직
}
* 흔히 사용하고 있는 router도 이벤트 기반으로 동작하고 있다.
이벤트에 따라 호출되는 콜백함수를 관리하는 것이 바로 이벤트 루프다.
Node가 여러 비동기 작업을 관리 하고, 처리 하고, 이벤트를 관리하는데 필수적인 역할을 하는 구현체 이다.
비동기 작업들을 모아서 관리하고 순서대로 실행할 수 있게 해주는 관리 도구인 것.


위의 그림과 같이 이벤트 루프는 총 6개의 페이즈로 구성되어 있다.
한 페이즈에서 다음 페이즈로 넘어가는 것을 Tick이라고 부른다.
각 페이즈는 자신만의 큐를 관리하고 있다.
이벤트 루프는 라운드 로빈(round-robin) 방식으로 노드 프로세스가 종료될때까지 일정 규칙에 따라 여러개의 페이즈들을 계속 순회한다. 페이즈들은 각각의 큐들을 관리하고, 해당 큐들은 FIFO(First In First Out) 순서로 콜백함수들을 처리한다.
Node가 이벤트 루프로 비동기를 지원하기는 하지만, 각 페이즈는 하나씩 완료하고 다음페이즈로 넘어간다. 결국엔 node가 싱글 스레드 이기 때문에 poll 페이즈를 실행함과 동시에 check페이즈를 실행하고 이런건 불가능하다.
위의 이슈에서 발생하는 문제가 있는데 한 페이즈에 쌓인 작업을 처리하면서 계속 그 페이즈에 새로운 작업이 쌓이거나, 새로 스케쥴링이 되면 그 페이즈가 완료될 때까지 node는 해당 페이즈를 벗어나지 못한다. 하지만 시스템 실행한도에 영향을 받기 때문에 영원히 갇히는 일은 없다.
각 페이즈의 역할
- 타이머 단계 (Timers Phase):
- 이 단계에서는 setTimeout()과 setInterval()로 등록된 콜백 함수가 실행된다. 타이머의 콜백은 정해진 시간 이후에 큐에 추가된다.
- I/O 콜백 단계 (I/O Callbacks Phase):
- 비동기 I/O 작업의 콜백 함수가 이 단계에서 실행된다. 예를 들어, 파일 읽기 작업이나 네트워크 요청과 같은 작업의 콜백이 여기에 해당한다.
- Idle, Prepare 단계 (Idle, Prepare Phase):
- 내부적으로 Node.js가 사용한다. 개발자는 이 단계를 직접 다루지 않는다.
- Poll 단계 (Poll Phase):
- 이 단계에서 이벤트 루프는 I/O 작업의 완료를 기다린다. 큐에 있는 I/O 콜백을 실행하고, 새로 들어온 I/O 작업을 처리한다. 이 단계에서 I/O 작업이 없으면, 타이머를 기다린다.
- Check 단계 (Check Phase):
- setImmediate()로 등록된 콜백이 이 단계에서 실행된다. setImmediate()는 현재 이벤트 루프 사이클이 끝난 후에 실행된다.
- Close Callbacks 단계 (Close Callbacks Phase):
- socket.on('close', ...)과 같은 콜백이 이 단계에서 실행된다. 이 단계에서는 연결이 닫힐 때 실행되어야 하는 콜백을 처리한다.
예시와 함께하는 이벤트 루프 동작 (ex. test.js)
- node test.js 명령어를 우리가 터미널에 입력한다
- node는 이벤트 루프를 생성한다.
- 생성한 이벤트 루프에 진입하지 않고 이벤트 루프 바깥에서 test.js 파일을 처음부터 끝까지 차례대로 실행한다.
- 이벤트 루프가 살아있는지 확인한다. (이벤트 루프에 작업이 남아있는지 확인한다.)
- 작업이 남아있다면 이벤트 루프에 진입한다.
- 각 페이즈를 돌면서 이벤트 루프가 실행가능한 상태인지 확인하고 실행하고, 해당 이벤트 루프가 새로운 콜백을 만들어내면 새로운 콜백을 해당하는 페이즈에 집어넣는다.
- 이벤트 루프가 빌때까지 4, 5, 6을 반복한다.
- 이벤트 루프에 작업이 남아있지 않으면 이벤트 루프를 종료하고 프로그램을 종료한다.
<참고>
Node.js 동작원리 (Single thread, Event-driven, Non-Blocking I/O, Event loop)
Node.js는 어떻게 동작하는가
medium.com
Node.js 내부 구조와 동작 방식
이번에 가벼운 프로젝트를 하면서 Node.js를 사용하게 되었다.기존에 진행한 프로젝트에서 Node.js를 빈번히 사용해 왔지만, Node.js의 내부 구조와 동작 방식에 대해서는 전혀 알지 못하고 그저 사용
velog.io
'개발지식 > Node.js' 카테고리의 다른 글
| [express] express-generator 폴더 구조 및 파일 역할 (0) | 2025.09.22 |
|---|---|
| [Express] Express의 정의와 기본 구조 (0) | 2025.09.16 |
| [Node.js] Node.js 기본 생태계 (0) | 2025.09.15 |
| [Node.js] fs모듈 - readFile(), readFileSync() (0) | 2025.09.10 |