[JS] Node.js의 아키텍처

개구링·2021년 8월 23일
15
post-thumbnail

# Node.js란?

확장성 있는 네트워크 애플리케이션(특히 서버 사이드) 개발에 사용되는 소프트웨어 플랫폼이다. 작성 언어로 자바스크립트를 활용하며 논블로킹(Non-blocking) I/O단일 스레드 이벤트 루프를 통한 높은 처리 성능을 가지고 있다.

내장 HTTP 서버 라이브러리를 포함하고 있어 웹 서버에서 아파치 등의 별도의 소프트웨어 없이 동작하는 것이 가능하며 이를 통해 웹 서버의 동작에 있어 더 많은 통제를 가능케 한다.

V8(자바스크립트 엔진)으로 빌드된 이벤트 기반 자바스크립트 런타임이다.


출처: 위키백과

일반적으로 NodeJS에 대해 설명할 때 논블로킹 I/O 모델을 사용하고 비동기 프로그래밍 스타일을 지원하는 자바스크립트 런타임 환경이라고 정리할 수 있습니다.
이 글에서는 자바스크립트의 동작 원리와 함께 이러한 NodeJS의 특성에 대해 자세히 다뤄보고자 합니다.



# Javascript 동작 원리

자바스크립트 엔진

자바스크립트는 싱글스레드로 동작합니다.
즉, 자바스크립트 엔진의 메모리 구조는 하나의 콜스택과 메모리 힙으로 이루어져있습니다.

콜 스택(Call Stack)

  • 코드가 호출되면서 스택으로 쌓이는 곳
  • 원시타입 값과 함수 호출의 실행 컨텍스트를 저장

메모리 힙(Memory Heap)

  • 메모리 할당을 담당
  • 참조타입 값(객체, 배열, 함수 같은 크기가 동적으로 변할 수 있는 값)을 저장

자바스크립트 런타임

🙄 자바스크립트가 싱글스레드일때 생기는 의문점

1. 자바스크립트가 싱글스레드라면 런타임도 싱글스레드라는 말인가?

자바스크립트 엔진은 단지 코드에 대한 실행환경입니다. 실제로 이벤트들을 처리하는 것은 브라우저나 NodeJS와 같은 런타임 환경이고, 이러한 런타임 환경에서 자바스크립트 엔진을 구동하는 것입니다.

👉 정리

  • 자바스크립트 엔진 ⇒ 싱글스레드
  • 자바스크립트 엔진을 구동하는 런타임 ⇒ 싱글스레드일수도, 아닐수도
    (이 부분은 이곳을 참고)

2. 싱글스레드로 어떻게 여러 요청을 동시에 받을 수 있는 걸까?

브라우저나 NodeJS 같은 런타임 환경의 도움을 받아서 여러 요청을 처리할 수 있는 이유는 바로 자바스크립트가 비동기적으로 코드를 실행하기 때문입니다. 덕분에 하나의 요청이 완료되기까지 기다리지 않고 동시에 다른 작업을 실행할 수 있습니다.

참고로 자바스크립트 런타임 자체에서는 비동기 API를 지원하지 않습니다. 동시성을 보장하는 비동기, 논블로킹 작업들은 런타임 환경에서 담당하는 것입니다.

👉 정리

  • 자바스크립트→ 비동기 코드를 실행
  • 런타임 환경→ 비동기 API 지원

3. 자바스크립트는 왜 자체적으로 멀티스레드를 쓰지 않을까?

단순하게 생각해보면 자바스크립트가 싱글스레드가 아닌 멀티스레드 환경이라면 자체적으로 여러 작업을 동시에 처리할 수 있을 것입니다.

그럼에도 불구하고 싱글스레드를 고집한다는 것은, 오히려 싱글스레드 환경에서 비동기 코드를 사용하는 것이 멀티스레드 환경에서의 복잡한 문제들(예를들면 데드락)을 신경쓰는 것 보다 낫다라고 판단했기 때문입니다.

👉 정리

  • 싱글스레드 + 비동기 코드가 멀티스레드에서의 복잡한 문제들을 신경쓰는 것 보다 낫다!
    ⇒ 사용하기 쉬워서



# Node.js 내부 구조

NodeJS는 크게 나눴을 때

  • Node.js Core Library (내장 라이브러리)
  • V8 (자바스크립트 엔진)
  • libuv (논블로킹 I/O, 이벤트 기반의 라이브러리)

로 구성됩니다.




# Node.js 동작 원리

NodeJS의 내부 구조에서 핵심적인 부분은 무엇일까?

대부분 NodeJS가 싱글스레드라고 말하는 이유는 V8엔진(자바스크립트 엔진)을 사용하기 때문입니다.
하지만 NodeJS는 자바스크립트 기반의 V8엔진만 있는 것이 아닙니다.
V8엔진 뒤에서 NodeJS의 작업을 처리하고 있는 C++ 기반의 libuv라는 라이브러리에 더 주목할 필요가 있습니다.


논-블로킹(Non-Blocking) I/O

논-블로킹 I/O란, 데이터 전송을 마치기 전에
기타 프로세스가 계속 작업하도록 하는 비동기 입출력 처리를 말합니다.

I/O 작업들은 OS 커널 또는 libuv의 스레드 풀에서 담당하게됩니다.

libuv는 OS 커널이 해당 작업을 지원하고 있는지 확인하고,
OS 커널이 지원하지 않는 작업은 스레드 풀로 던져줍니다.

즉, libuv는 이러한 I/O 관련 작업을 백그라운드에서 수행하도록 처리해주고,덕분에 이벤트루프는 계속해서 다른 일을 처리할 수 있게 됩니다.


스레드 풀(thread pool)

스레드 풀은 비동기 I/O 라이브러리를 통해 I/O 관련 작업을 비동기로 처리합니다.

NodeJS에서 동기 작업을 실행하는 경우

⇒ 이 작업은 항상 메인스레드(싱글스레드인 V8엔진)에서 실행됩니다.

NodeJS에서 비동기 작업을 실행하는 경우

⇒ 이 작업이 메인스레드에서 실행되지 않을 수도 있습니다.

해당 작업이 어떤 작업인지에 따라 다른 스레드(스레드 풀)로 분기되어 처리될 수 있기 때문입니다.
(libuv의 스레드 풀은 기본적으로 4개의 스레드를 가지는 멀티스레드입니다.)



# 마무리

NodeJS에 대해 정리해보자면..

1. 논-블로킹 I/O 모델을 사용

2. 자바스크립트의 비동기 작업을 처리하는 런타임 환경

3. 내부적으로 싱글스레드(이벤트루프) 일 수도, 멀티스레드(이벤트루프 외부의 스레드 풀) 일 수도 있다


사실 NodeJS가 웹서버로 사용되는 것에 대해 조사하다보니 OS까지 파고들게 되었습니다..
전체적인 구조를 모두 다루기보다는 핵심적인 부분에 집중하여 글을 작성하였습니다.
구글링을 통해 관련된 블로그 글들을 참고하며 이해한 바를 정리한 글이니 사실과 다른 내용은 언제든 알려주시면 감사하겠습니다😅

그동안 자바스크립트가 왜 싱글스레드이고, 비동기 상황이 런타임에서 어떻게 처리되는지에 대해서는 깊숙하게 고민해본 적이 없었는데, 이번 기회로 앞으로 자바스크립트 프로그램을 다룰 때 내부 동작 원리를 떠올릴 수 있을 것 같습니다.


🔗 ref

NodeJS 동작 원리
NodeJS 내부 구조
자바스크립트는 왜 싱글 쓰레드일까?
Node.js Event-loop Architecture
libuv
NodeJS Event Loop

profile
기록을 취미로

2개의 댓글

comment-user-thumbnail
2021년 9월 1일

Node.js로 개인 프로젝트를 진행해 봤으면서도 libuv라는 라이브러리의 존재는 처음 알게 되었습니다! 정말 감사합니다!

1개의 답글