본문 바로가기

카테고리 없음

JavaScript와 Node.js 란 무엇인가? node.js의 특징은?

 

JavaScript란?

JavaScript가 존재하기 이전의 웹 생태계는 웹 브라우저가 존재했지만, HTML과 CSS로 아주 간단한 스타일만 적용하는게 일반적이었다.

 

CSS 또한 지금처럼 화려하게 적용 할만한 기능이 없었고, 현재의 워드프로세서보다 더 적은 기능으로 텍스트를 꾸미고 간격을 조정하는게 전부였던 시절이다.

 

HTML이라는 단어 그대로 [Hyper Text]를 이용해 각 문서(페이지)간 이동이 원활하게 꾸며둘 수 있는 순수한 [Markup Language]로서 이용되었다.

순수하게 문서를 웹으로 볼 수 있는 수준으로 이용한것이다.

 

1999년대의 아마존 사이트, 마치 전자책을 보는 것과 같은느낌이 든다.

 

 

JavaScript 탄생 비화

자바스크립트는 이런 정적인(Static) 문서를 조금 더 동적(Dynamic)으로 표현할 수 있도록 만들어졌는데, NetScape라는 웹 브라우저를 통해 상호작용을 강조하기 위해 간단한 기능을 넣어 만든것이 자바스크립트의 시초가 되었다.

 

 

정적(Static) 페이지와 동적(Dynamic) 페이지란?

[정적 웹 페이지] (Static Web Page)

미리 작성되어 확정된 페이지로 실시간 정보나 맥락을 반영하지 못한다.

예를 들어, 항상 같은 사진과 함께 같은 링크로 연결되는 하이퍼링크만 존재하는 페이지는 정적

이 경우, 연산이 필요없으므로 Javascript로 프로그래밍할 이유가 없고 HTML로만 작성 한다.

 

[동적 웹 페이지] (Dynamic Web Page)

맥락, 상황을 반영할 수 있는 페이지를 말한다.

예를 들어, 현재 로그인한 유저의 프로필 사진이 화면에 표시되는 페이지는 동적이고, 다른 유저가 접속했다면 그 정보를 바탕으로 프로필 사진 또한 다른 사진으로 변경되기 때문이다.

 

 

브라우저별 언어와 표준화 하기

이 당시에는 NetScape가 만든 언어가 자바스크립트라는 이름도 아니었고, NetScape를 시작으로 브라우저를 만드는 각 회사에서 자신들의 브라우저에서 좀 더 다이나믹한 상호작용을 위해 언어를 사용할 수 있게 기능을 추가하기 시작했다.

 

이후에 NetScape 외에도 우리에게 익숙한 Internet Explorer같은 다른 브라우저들도 비슷한 언어를 개발하기 시작했는데, 브라우저마다 개발하는 방식이 달랐고 이게 불편해서 표준화 시키는 작업이 필요했다.

그 결과 [Ecma International] 에서 ECMAScript(ECMA-262)라는 이름으로 현재의 자바스크립트 표준을 제정하기 시작했다.

ECMAScript는 매년 꾸준히 변경되고 새로운 기능이 추가되면서 버전이 새로 나오고 있다.

 

. 2024년 5월 기준으로 최신은 2023년 6월에 발표된 ECMAScript 2023이 최신버전이며 14번째 에디션이다.

 

결국 우리가 현재 사용하는 자바스크립트는 ECMA International에 의해 제정된 표준인 ECMAScript로 구현된 것이고, 바로 이 자바스크립트 덕분에 동적 웹 페이지의 시대가 도래하게 된 것이다!

 

 

Node.JS 란 무엇인가? Node.JS의 구조

Node.js는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임이다.

Node.js는 이벤트 기반, 논 블로킹 I/O 모델을 사용해 가볍고 효율적이다.

Node.js 패키지 생태계인 npm은 세계에서 가장 큰 오픈 소스 라이브러리 생태계이다.

 

Node.js는 “Javascript를 브라우저가 아닌 컴퓨터에서 브라우저 없이 실행하게 도와주는 환경”이며, 이는 핵심적인 코어 라이브러리와 [V8 엔진]그리고 [libuv]라는 라이브러리를 통해 가능하다.

 

V8 엔진은 구글이 개발하여 구글 크롬 브라우저에서 사용하는 자바스크립트 엔진이다.

Node.js에서 이 엔진을 활용함으로써, 우리는 브라우저 환경 이외에서도 자바스크립트를 사용할 수 있다.

 

libuv는 Node.js가 비동기 I/O 작업을 수행할 수 있게 해주는 중요한 라이브러리이며, 이 라이브러리 덕분에 Node.js는 논 블로킹 I/O 모델이라는 특징을 가지게 되었고, 이를 통해 여러 요청을 효율적으로 처리할 수 있다.

 

이 두 가지 주요 구성 요소인 V8엔진과 libuv는 C와 C++ 언어로 작성되어 있지만, Node.js 사용자는 이에 대해 깊이 알 필요가 없다. 왜냐하면 Node.js가 이들과의 연결 및 상호 작용을 자동으로 처리해주기 때문입니다. 따라서 사용자는 자바스크립트를 이용해 Node.js를 활용하며 다양한 어플리케이션을 개발할 수 있다.

 

 

Node.js의 장점

글로벌 웹 프레임워크 시장에서의 높은 점유율

Node.js는 [2023년 스택오버플로우 설문]에 따르면 웹 프레임워크 기술 부문에서 1위를 차지할 정도로 대표적인 웹 프레임워크 기술이다.

Node.js는 대표적으로 논 블로킹(Non-blocking) I/O, 싱글 스레드(Single Thread), 이벤트 루프(Event Loop)의 특장점이 있다. 아래에서 자세히 살펴보자.

 

 

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

블로킹(Blocking) I/O와 논 블로킹(Non-blocking) I/O는 프로그램의 실행 흐름을 제어하는 방식이다.

블로킹 방식은 프로그램이 특정 작업을 수행하는 동안 다른 작업을 중단시키는 방식이고, 논 블로킹 방식은 프로그램이 여러 작업을 동시에 처리할 수 있는 장점이 있다.

 

I/O는 데이터의 입력(Input) / 출력(Output)을 의미하며, 파일을 저장하거나, 불러오는 것을 “I/O 처리를 한다.” 라고 표현

 

- 블로킹(Blocking) I/O

한 번에 하나의 작업만을 처리할 수 있음

호출된 함수가 자신의 작업을 모두 마칠 때 까지 호출한 함수에게 제어권을 넘겨주지 않음

예를 들어, 파일을 읽는 동안에는 다른 작업을 함께 수행할 수 없다.

 

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

시스템 호출이 완료되기를 기다리지 않고 바로 다음 작업으로 즉시 넘어갈 수 있는 방식

호출된 함수는 작업의 완료 여부와 상관없이 즉시 제어권을 호출한 함수에게 반환

예를 들어, 파일을 읽는 동안에도 동시에 연산을 하는 것처럼 다른 작업을 함께 수행 한다.

 

 

싱글 스레드(Single Thread)

싱글 스레드(Single Thread)는 스레드 하나를 사용하는 것이며, 동시에 하나의 작업만을 처리할 수 있다.

 

여기서 스레드(Thread)란 프로그램이 동작할 때, CPU 또는 프로세서를 사용하는 단위로, 여러 스레드를 사용하면 여러 작업을 동시에 처리할 수 있습니다. 그러나 이는 복잡성을 증가시키고, 리소스를 더욱 많이 소모하게 되는 문제가 발생한다.

 

Node.js는 싱글 스레드로 동작하지만, I/O 작업이 발생한 경우 이를 비동기적으로 처리하여 여러 작업을 동시에 처리할 수 있게 한다.

 

 

왜 싱글 스레드죠?

1개의 프로그램에서 여러개의 스레드를 사용할 수 있다면 연산할 수 있는 시간을 단축 시킬 수 있는데, 그럼에도 불구하고 Node.js가 싱글 스레드를 고집하는 이유는 무엇일까?

 

싱글 스레드는 많은 이점이 있다. 스레드 생성과 관리에 드는 부담을 줄여주며, 컴퓨팅 리소스를 효율적으로 사용할 수 있고, 한 번에 하나의 작업만 처리하기 때문에 동시성 문제, 즉 여러 스레드가 공유하는 자원에 대한 동시접근 문제, Race condition문제를 방지한다.

 

예를 들어, 웹 서버에서 동시에 많은 요청을 처리해야 하는 경우, 멀티스레드 방식을 사용하면 각 요청마다 스레드를 생성해야 하므로 오버헤드가 크게 발생하는데, Node.js의 싱글 스레드 + 이벤트 루프 방식을 사용하면, 동시에 많은 요청을 효율적으로 처리할수 있다.

 

이러한 특징 덕분에 연결 요청의 부하에 따른 서버 확장이 매우 자유로와서, Node.js는 특히 웹 서버에 특화된 철학이 녹아있는 언어라고 할 수 있다.

 

아무리 싱글 스레드만의 장점이 있다고 하더라도, 싱글 스레드 방식은 CPU 집중적인(CPU Intensive) 작업에는 적합하지 않은데, 이런 경우에는 멀티스레드 방식이 더욱 효율적일 수 있다.

물론 이러한 약점을 극복하기 위해 Node.js 는 추가 스레드를 생성하는 방법들을 제공하지만, 기본적으로 Node.js의 철학은 싱글 스레드 방식에 기반하고 있음을 기억해야 한다.

 

 

호출 스택(Call Stack)

호출 스택(Call Stack)은 함수의 실행 순서를 추적하는 자료구조다.

JavaScript는 코드를 실행하면서 호출 스택(Call Stack)에 함수를 추가(push)하고 함수가 완료되면 호출 스택에서 제거(pop)한다.

이는 비동기 작업에서 문제가 되는데, 특히 네트워크 요청과 같이 시간이 많이 걸리는 작업을 기다리는동안 JavaScript는 다른 어떠한 작업도 처리할 수 없게 되는데, 이 문제를 해결하기 위해 JavaScript는 이벤트 루프(Event Loop)와 이벤트 큐 (Event Queue)를 사용한다.

 

 

이벤트 루프(Event Loop)

 

이벤트 루프(Event Loop)는 여러 이벤트들과 같은 비동기 작업들을 모아서 관리하고, 어떤 순서대로 실행해야하는지 도와주는 도구라고 볼 수있다.

 

이벤트 루프(Event Loop)는 호출 스택(Call Stack)과 이벤트 큐(Event Queue)를 관찰하며, 호출 스택이 비어있고, 이벤트 큐에 작업이 있다면, 이벤트 큐의 작업을 호출 스택으로 이동하는 역할을 담당한다.

이벤트 루프를 활용하면, 자바스크립트는 시간이 오래 걸리는 작업을 이벤트 큐에 넣어 비동기적으로 처리하고, 그 동안 호출 스택에서 다른 작업들을 계속 처리할 수 있다.

 

 

이벤트 루프의 동작 방식 이해하기

function firstFunction() {
  console.log('firstFunction 입니다.');
  secondFunction();
}

function secondFunction() {
  // 2 초간 기다린다.
  setTimeout(function () {
    console.log('secondFunction 입니다.');
  }, 2000);
}

firstFunction();
console.log('전역 코드 실행 중!');

// print: firstFunction 입니다.
// print: 전역 코드 실행 중!
/** 2 초간 기다린다. **/
// print: secondFunction 입니다.
 
 

위 예제 코드에서 firstFunction은 호출 스택에 추가(push)되고, 실행되며 ‘firstFunction 입니다.’를 콘솔에 출력하고 secondFunction을 호출 한다. 그 다음 secondFunction은 호출 스택에 추가되고, setTimeout 함수를 실행 한다.

 

setTimeout은 비동기 함수이므로, JavaScript는 이 작업을 이벤트 큐에 넣고 secondFunction을 호출 스택에서 제거(pop)합니다. 그 다음 firstFunction도 호출 스택에서 제거하고, ‘전역 코드 실행 중!’을 콘솔에 출력합니다.

 

2초가 지난 후, ‘secondFunction 입니다.’를 출력하는 작업이 이벤트 큐에서 호출 스택으로 이동하고 실행됩니다. 이때 호출 스택은 비어 있기 때문에 이벤트 루프가 이 작업을 호출 스택으로 이동시킬 수 있었습니다. 이처럼, 이벤트 루프와 이벤트 큐를 사용함으로써 JavaScript는 비동기 작업을 처리할 수 있게된 것이다.