nodejs
nodejs란 Chrome V8 Javascript 엔진으로 빌드된 Javascript 런타임입니다. 런타임이라는 말은 javascript를 실행하는 환경입니다. 또한 Nodejs로 웹사이트에서 서버로 사용되는 건 무슨 말일까요? nodejs에 대한 기초 개념을 함께 알아가보도록 합시다. 이 글을 목적은 초급과 중급을 위한 글입니다. 초급의 기준은 express를 들어는 봤다 정도이고, 중급의 기준은 nodejs express를 활용하여 web server를 만들 수 있는 기준입니다. 고급의 기준은 프로세스와 스레드 관리는 자유롭게 할 수 있음을 의미합니다. 사실 고급은 이 글을 볼 필요가 없으니 초급과 중급 기준으로 기초를 탄탄하게 만들기 위해 작성한 글입니다.
개발자가 보통 말하는 nodejs란 무엇일까요? 우리는 보통 nodejs를 웹 서버라고 이해합니다. 하지만, nodejs는 웹 서버를 만들 수 있는 기능을 제공할 뿐이지, 웹 서버는 아닙니다. 공식문서에 들어가 보면, Node.js는 크롬 V8 자바스크립트 엔진으로 빌드된 자바스크립트 런타임이라고 설명이 되어 있습니다. 쉽게 말하면, js 파일을 실행하기 위해 필요한 환경이 nodejs입니다.
여기서 주의해야 할 점은 html의 javascript와 nodejs가 실행하는 javascript가 다르다는 것을 입니다. nodejs 이전의 javascript는 .js 파일을 단독으로 실행하지 못했고, 웹의 브라우저에서만 javascript를 실행할 수 있었습니다. nodejs라는 javascript 실행기의 등장으로, 현재 우리가 VScode에서 npm run start / node index.js 와 같은 방식으로 javascript 파일을 실행할 수 있게 되었습니다. 정리하면, nodejs이전에는 html에서 브라우저가 javascript를 읽은 것이고, nodejs 이후에는 nodejs 파일명.js 로 javscript도 실행 할 수 있는 방법이 생긴 것이라고 할 수 있습니다. 이러한 변화로 인해 javascript를 독립적으로 실행이 가능해졌고, javascript를 활용한 웹 서버를 만들고 실행 할 수 있게 되었습니다.
Node.js는 현대 웹 개발에서 중요한 역할을 하고 있습니다. 이벤트 루프오 논블로킹 I/O모델을 활용하여 높은 처리량과 낮은 지연 시간을 가지기 때문에 효율적인 네트워크 Application 개발을 가능하게 합니다. 단일 스레드이지만 이벤트 기반 모델과 비동기 처리 덕분에 높은 성능을 유지할 수 있으며, 특히 I/O가 집중적인 웹 애플리케이션에 좋은 성능을 가집니다.
공식문서에서는 아래와 같이 nodejs를 소개합니다.
As an asynchronous event-driven JavaScript runtime, Node.js is designed to build scalable network applications.
비동기 이벤트 기반이란, 수행할 동작을 메모리에 미리 등록해 두고 이벤트가 발생할 시 지정된 작업을 수행하는 것을 의미합니다. 이때 이벤트란 특정 행위라고 할 수 있다. API 요청이나 타이머, 버튼 클릭과 같이 nodejs에 입력을 주는 것을 의미합니다.
nodejs의 모든 API는 비동기로 non-blocking이라고 할 수 있습니다. 쉽게 말하면 nodejs에서의 API 요청 시 반환되는 데이터를 기다리지 않고 다음 API 작업을 수행합니다. 이러한 작업 프로세스는 nodejs는 내부적으로 이전에 보낸 API에 대한 응답을 받는다고 할 수 있습니다. 아래의 코드만으로 우리는 웹 서버를 만드는 것이 가능합니다.
const http = require('http')
const hostname = '127.0.0.1'
const port = 3000
const server = http.createServer((req, res) => {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('Hello World')
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
기본적으로 모든 연결(localhost:3000 API 요청)은 동시에 처리됩니다. 연결 시 콜백 함수가 실행되고, 연결이 없을 때는 노드는 sleep(절전모드) 상태로 전환 됩니다.
당연한 이야기지만, nodejs는 Google Chrome's V8 JavaScript Engine으로 만들어졌기 때문에 매우 빠릅니다. 물론 다른 빠른 라이브러리와 프레임워크들이 생겨나고 있기 때문에 상대적으로 빠른 것이지 매우 빠르진 않습니다.
nodejs는 http 라이브러리는 제공하기 때문에, 아파치와 같은 별로 소프트웨어가 없어도 웹 서버를 작동시키는 것이 가능합니다. 따라서 기존의 웹 서버의 통제 없이 자유롭게 핸들링이 가능하기 때문에 높은 확장성을 가집니다.
nodejs는 단일 스레드를 사용하기 때문에, 기존의 다른 서버보다 더 많은 수의 요청을 처리할 수 있습니다. 이 개념을 이해하려면, 우선 스레드와 프로세스의 차이부터 이해를 해야 합니다. 프로세스는 운영체제가 할당하는 작업이고, 스레드는 프로세스 내에서 실행되는 작업단위입니다. nodejs가 실행되면, 프로세스가 하나 실행되게 됩니다. 그리고 프로세스 내부의 스레드가 작업을 진행합니다. 사실 프로세스 안에는 여러 개의 스레드가 있지만, 우리가 핸들링할 수 있는 스레드는 하나이기 때문에 nodejs를 단일 스레드라 합니다. 그렇다면 왜 단일 스레드가 더 많이 처리할까요? 사실은 nodejs의 컨셉은 멀티스레드는 구현하기 어렵지만, 일반적인 경우에는 효율적으로 쉽고 빠르게 구현 할 수 있습니다. nodejs는 모든 처리를 순차적으로 처리하는 단일 스레드가 아닙니다. 단일 스레드이지만, 백그라운드 영역에서 동시처리(ex. task queue 등)를 진행하기 때문에 일반적인 단일 스레드와 멀티 스레드의 장점을 다 가지고 온 것이라 볼 수 있습니다. 관련 내용은 추가로 다룰 예정입니다.
데이터를 보낼 때, 한 번에 보내는 방식과 데이터를 쪼개서 여러 번 보내는 방식이 있습니다. 일반적인 버퍼(Buffering) 방식은 파일을 한 번에 보내는 방식을 의미합니다. nodejs는 데이터를 한번에 보내는 게 아니라 덩어리로 나눠서 보냅니다. 이는 우리가 파일 다운로드하는 방식과 비슷하다고 생각하면 됩니다. 관련 내용은 추가로 다룰 예정입니다.
Nodejs의 장접을 극대화 하는 방식으로는 비동기 I/O 작업을 효율적으로 관리하는 것입니다. 비동기 I/O처리는 논블로킹 작업을 가능하게 하여 Application 작업량과 응답 시간을 개선할 수 있습니다.
Nodejs 클러스터 모듈을 사용합니다. 단일 Node.js 인스턴스가 멀티 코어 시스템의 모든 CPU 코어를 사용할 수 있게 됩니다. 이는 높은 트래픽 처리하는 Application에서는 큰 장점이 됩니다.
성능을 모니터링합니다. 일반적인 PM2/Clicin/Relic과 같은 다양한 성능 모니터링이 존재합니다. 이는 성능 모니터링을 하고 병목 현상을 식별하여 최적화에 도움을 줄 수 있습니다.