Node.js 동작 원리 톺아보기

원민관·2025년 10월 25일

[TIL]

목록 보기
199/201
post-thumbnail

1. Node.js 소개 👨‍💻

Node.js는 서버 사이드 자바스크립트 런타임 환경입니다.

런타임 환경은 프로그래밍 언어가 구동되는 환경입니다. 자바스크립트가 브라우저에서 실행되기에 우리는 웹 서비스를 이용할 수 있죠. 이때의 자바스크립트 런타임 환경은 브라우저라고 할 수 있습니다. 브라우저에서 자바스크립트라는 프로그래밍 언어가 구동되기 때문입니다. 브라우저는 클라이언트 사이드 자바스크립트 런타임 환경이라고 볼 수 있겠네요.

Node.js는 자바스크립트 코드를 브라우저 밖에서 실행할 수 있게 해주는 런타임 환경입니다. 자바스크립트라는 프론트엔드 필수 언어로 백엔드까지 작성할 수 있다는 엄청난 장점 덕분에 높은 점유율을 확보할 수 있었죠. Node.js는 오픈 소스 자바스크립트 엔진인 크롬 V8에, 비동기 이벤트 처리 라이브러리인 libuv를 결합하여 구현되었습니다.

2. Node.js 구성 요소 👨‍💻

Node.js는 계층 구조로 설계되어, 하단 계층에 있는 API를 사용합니다. 예시를 들어보겠습니다.

  1. 애플리케이션 - fs.readFile('data.txt', 'utf8', callback) 코드 작성
  2. Node.js API - fs 모듈이 readFile 함수를 JavaScript로 제공
  3. Node.js 바인딩 - JavaScript의 readFile 호출을 C++ 파일 읽기 요청으로 변환
  4. Node.js 표준 라이브러리 - C++로 파일 읽기 로직 처리
  5. libuv - 비동기로 파일 읽기 작업을 스레드풀에 요청하고 완료되면 콜백 실행
  6. 저수준 라이브러리 - 운영체제의 파일 시스템 API 호출 (예: read 시스템 콜)
  7. 운영체제 - 실제 디스크에서 'data.txt' 파일 데이터를 읽어서 반환

3. V8 엔진 동작 원리 👨‍💻

V8 엔진은 사용자가 작성한 자바스크립트 코드를 기계어로 변환하여 실행하는 역할을 합니다. 기계어로 변환하는 과정을 컴파일이라고 합니다.

  1. Source: 개발자가 작성한 JavaScript 원본 코드입니다.
  2. Parser: 원본 코드를 V8 엔진이 이해할 수 있는 토큰으로 분해하고 분석합니다. AST(Abstract Syntax Tree)를 생성합니다.
  3. Ignition: AST를 Bytecode라는 중간 단계의 코드를 생성합니다. Bytecode는 인터프리터(Ignition)에 의해 바로 실행될 수 있습니다.
  4. SparkPlug: 코드를 빠르게 실행하기 위해, Bytecode를 최적화되지 않은(Non-Optimized) 기계 코드로 신속하게 컴파일합니다.
  5. TurboFan: 코드가 반복적으로 실행되면서 어떤 부분이 자주 사용되는지 파악합니다. 자주 사용되는 코드만 골라서 매우 효율적인, 고도로 최적화된(Optimized) 기계 코드로 컴파일할 수 있습니다.
  6. Deoptimize: 만약 TurboFan이 최적화한 코드의 가정이 틀어지면, V8은 최적화된 기계 코드를 버리고 다시 SparkPlug가 만든 비최적화 코드나 Ignition의 Bytecode로 돌아가서 실행을 이어갑니다.

4. libuv(이벤트 루프) 동작 원리 👨‍💻

libuv를 통해 이벤트 루프와 운영체제 시스템 API를 사용할 수 있습니다. 핵심은 이벤트 루프입니다.

  1. 이벤트 루프는 여러 개의 FIFO Queue로 이루어져 있습니다.
  2. Timer 단계는 setTimeout(), setInterval()을 처리합니다.
  3. Pending 단계는 다음 반복으로 연기된 콜백을 처리합니다.
  4. Idle / Prepare 단계는 내부적으로만 사용합니다.
  5. Poll 단계는 소켓 연결, 파일 읽기 등의 작업을 수행합니다.
  6. Check 단계는 setImmediate()를 처리합니다.
  7. Close 단계는 콜백의 종료 처리를 합니다.
  8. microTaskQueue와 nextTickQueue에 있는 작업은 각 단계마다 우선적으로 실행합니다.(nextTickQueue의 작업의 우선순위가 microTaskQueue 작업의 우선순위보다 높습니다.)

5. Node.js 전체 아키텍처 👨‍💻

  1. 점선은 libuv 영역을 의미합니다.
  2. 애플리케이션에서 요청이 발생합니다. V8 엔진은 자바스크립트 코드를 바이트 코드나 기계어로 변환합니다.
  3. 자바스크립트로 작성된 Node.js의 API는 C++로 작성된 코드를 사용합니다.
  4. V8 엔진은 이벤트 루프로 libuv를 사용하고, 전달된 요청을 libuv 내부의 이벤트 큐에 추가합니다.
  5. 이벤트 큐에 쌓인 요청은 이벤트 루프에 전달되고, 운영체제 커널에 비동기 처리를 맡깁니다. 운영체제 내부적으로 비동기 처리가 힘든 경우는 워커 스레드에서 처리합니다.
  6. 운영체제의 커널 또는 워크 스레드가 완료한 작업은 다시 이벤트 루프로 전달됩니다.
  7. 이벤트 루프에서는 콜백으로 전달된 요청에 대한 완료 처리를 하고 넘깁니다.
  8. 완료 처리된 응답을 애플리케이션으로 전달합니다.
profile
Write a little every day, without hope, without despair ✍️

0개의 댓글