소스코드부터 이벤트 루프, V8 메모리, 스트림, 모듈 시스템까지 Node.js 내부를 추적하는 여정
면접 준비를 하다가 "Node.js가 뭔가요?"라는 질문에 제대로 답할 수 없다는 걸 깨달았습니다. 런타임이 뭔지, V8과 libuv가 각각 무슨 역할인지, 실제 Node.js GitHub 소스코드를 열어서 os.hostname() 한 줄이 OS까지 도달하는 과정을 추적해봤습니다.
Node.js의 libuv 스레드 풀을 이해하려면 OS 기초가 필요합니다. 프로세스와 스레드의 차이, CPU 코어와 소프트웨어 스레드의 관계, 메모리 동적 할당까지 — Node.js 동시성의 전제 지식을 정리합니다.
1편에서 Node.js의 내부 구조를, 2편에서 프로세스와 스레드의 기본 개념을 확인했습니다. 이번에는 "싱글 스레드인데 어떻게 동시 처리가 가능한가"라는 질문에 답하기 위해, 콜 스택과 이벤트 루프의 관계, libuv가 작업을 처리하는 두 가지 방식, 그리고 이벤트 루프 6개 페이즈의 실체를 소스코드로 확인해봤습니다.
이벤트 루프의 6개 페이즈를 하나씩 살펴봅니다. 타이머 힙의 동작 방식, Poll이 메인 무대인 이유, setTimeout(0)과 setImmediate의 순서가 달라지는 원리, 그리고 process.nextTick과 Promise가 매 페이즈 사이에 끼어드는 구조까지 정리합니다.
이벤트 루프 이론을 실제 NestJS 서버 코드에 대입합니다. 서버 시작부터 HTTP 요청 처리까지, 6개 페이즈가 실제로 어떤 역할을 하는지 추적합니다.
V8 엔진의 힙 메모리 구조와 가비지 컬렉션 동작 원리를 추적합니다. New Space의 Scavenge부터 Old Space의 Mark-Sweep-Compact까지, 객체가 생성되고 이동하고 제거되는 과정을 살펴봅니다.
Node.js 스트림의 4가지 타입과 동작 원리를 추적합니다. 1GB 파일을 왜 readFile 대신 createReadStream으로 읽어야 하는지, 백프레셔는 무엇인지, pipe가 내부에서 어떻게 동작하는지 살펴봅니다.
Node.js의 두 가지 모듈 시스템 — CommonJS의 require와 ESM의 import가 내부에서 어떻게 다르게 동작하는지 추적합니다. 로딩 시점, 캐싱, 순환 참조 처리의 차이를 살펴봅니다.