카테고리 없음

Node.js

Ynghan 2023. 6. 6. 02:03

Node.js

Node.js가 무엇인가?

Node.js는 Chrome의 V8 JavaScript 엔진에 구축된 오픈 소스 서버 측 런타임 환경입니다. JavaScript를 사용하여 확장성이 높은 서버 측 애플리케이션을 구축하기 위한 이벤트 기반의 비차단(비동기) I/O 및 교차 플랫폼 런타임 환경을 제공합니다.
Node.js는 명령줄 애플리케이션, 웹 애플리케이션, 실시간 채팅 애플리케이션, REST API 서버 등과 같은 다양한 유형의 애플리케이션을 구축하는 데 사용할 수 있습니다. 그러나 PHP, Java 또는 ASP.NET과 유사한 웹 서버와 같은 네트워크 프로그램을 만드는 데 주로 사용됩니다.

Node.js의 장점

Node.js는 MIT 라이선스 하에 있는 오픈 소스 프레임워크입니다.
(MIT 라이센스는 MIT(Massachusetts Institute of Technology)에서 시작되는 자유 소프트웨어 라이센스입니다.)
JavaScript를 사용하여 전체 서버측 응용프로그램을 작성합니다.
최소한의 모듈을 포함하는 경량 프레임워크.
응용 프로그램의 필요에 따라 다른 모듈을 포함할 수 있습니다.
기본적으로 비동기식입니다. 따라서 다른 프레임워크보다 빠른 성능을 제공합니다.
Windows, MAC 또는 Linux에서 실행되는 교차 플랫폼 프레임워크

학습 범위

서버 프로그래밍을 위한 Javascript의 내용

Web Server의 역할에 필요한 Node.js의 기능
 - Express 사용법
 - 템플릿 엔진을 React로 대체할 수 있으므로 제외

백엔드 개발 환경
 - JSON Server와 Postman

사례 연구

Node.js

  • 단순한 JS 엔진?
    • 고성능의 V8 엔진(JIT Compiler)을 서버에서도 수행될 수 있도록 개발
    • 클라이언트 언어와 서버 언어의 통일
    • 비동기 프로그래밍 구조를 제공하여 고성능
      • 그러나 개발이 어려움
    • 다양한 서버 개발 라이브러리 제공
      • mail, ftp, web, chatting 등등
      • express 대표적인 웹서버 라이브러리
    • 인터페이스
      • REPL(read-eval-print loop) or Shell도 제공
        • 그냥 자바스크립트 콘솔이라고 생각하면 된다.
        • 파이썬의 IDLE와 유사
      • to execute, just type node

왜 Flask가 아닌 Node.js인가?

  • 클라이언트와 동일한 언어
  • 유용한 다수의 라이브러리
    • ex. VB의 장점
  • 취업 시장
  • 요약
    • Flask를 사용하는 경우
      • 파이썬이라는 깔끔한 언어를 사용하여 프로그래밍의 생산성을 추구
      • node.js를 공부하고 싶지 않은 경우
    • Node.js
      • 취업을 고려할 때 : De Facto Standard, JSP(Sprint, Strut) → Node.js
      • 다양한 라이브러리를 사용해야 할 때

Node.js와 React의 비교

  • 차이점
    • Node.js는 Server Side Programming
    • React는 Client Side Programming
  • 정리
    • 특정 기능을 개발하는 경우 가능하면 Client에서 구현
    • 서버에서 개발하면
      • 네트워크 프로그래밍을 해야 하므로 개발 과정이 어렵고
      • 서버에서 수행되므로 성능이 저하된다.

최종 정리

  • 현재로는 node.js + react + etc. 가 표준이다.

기타

  • ejs : EJS stands for Embedded JavaScript
    • express에서 사용되는 Templating Language
    • react를 사용하면 필요가 없는 오래된 기술

Asyncronous Programming

  • Node.js의 필수 개념
  • 내가 아는 Node.js는?
    • Event-Driven 방법을 적용하여 고성능을 제공하고
    • 클라이언트와 동일한 언어인 Javascript를 사용하는 웹 서버 구축 라이브러리

기존의 모델과 문제점

  • 게임엔진 제작에서 시작
  • 게임엔진은 다양한 네트워크 서비스를 요구
    • 대부분은 네트워크 서비스가 요구됨
      • 게임 자체, Chatting, Remote File System 등
    • 구현 방식이 유사
      • core + alpha
  • Multi Processes 간의 자원 충돌 문제
  • 동시 사용자 문제

동기와 비동기

  • 성능 향상의 첫단추
  • 차이점
    • 클라이언트: 기능 즉 함수의 호출자
    • 서버: 기능을 수행해주는 함수 자체
  •  
  • 동기는 함수를 호출하면 수행이 완료될 때까지 대기
    • 비동기는 함수를 호출 만하고 다음 코드를 수행

이벤트 처리 방식

  • 처리 방법
    • CPU에서 1개의 Thread만 사용하여, Deadlocak를 방지
    • 클라이언트의 요청은 Queue에 대기
    • 서버가 시간이 될 때(idle 상태)
      • 클라이언트의 요청 중 계산 부분만 처리
      • I/O 처리 시작
      • 이것을 반복
    • 서버가 I/O 처리가 완료되면 클라이언트에게 결과를 반환
  • 효과
    • 16개의 코어가 있어도 하나의 스레드만 운영되어 성능이 저하되지만,
    • 어차피 거의 모든 시간을 I/O가 차지하므로 성능은 충분
      • IO bound Task인 경우는 고성능 제공
      • CPU bound Task에서는 문제가 많을 수 있음
    • 서버 성능 향상을 위한 모델
      • 성능이 1/16으로 감소하고 100만배가 증가
      • 이론상 약 6만배가 향상된다.
      • 실제로는 ??
        • 충분히 빠르다

Callback

  • call-back: “반대로 호출해 주세요”의 의미
  • 비동기 방식에서 호출한 함수가 완료됨을 알 수 있는 방법
var http = require('http');
var server = http.createServer(function(req, res){
  console.log("Sucess");
});
server.listen(5000);

Promise는 Callback Hell을 해결하기 위해 제안

  • Promise는 코드 생성과 코드 소비를 연결하는 JavaScript 객체입니다
    • "생성 코드"은 시간이 걸릴 수 있는 코드입니다.
      • 비동기 함수의 정의
    • "소비 코드" 는 결과를 기다려야하는 코드입니다.
      • 비동기 함수의 실행
  • Promise는 비동기 작업을 위한 Javascript 객체입니다.
    • 1. State : Pending, Resolve, Reject
      • 실행중, 해결, 거절
    • 2. Producer / Consumer
      • promise를 정의 / promise를 호출

Promise 문법

let myPromise = new Promise(function(myResolve, myReject) {
// "Producing Code" (May take some time)
  myResolve(); // when successful
  myReject();  // when error
});

// "Consuming Code" (Must wait for a fulfilled Promise)
myPromise.then(function(value) { /* code if successful */ },
)
.catch(
  function(error) { /* code if some error */ }
)

 

User defined Promise

  • 가장 기본적인 형태
  • promise에서 resolve()를 호출하면 then이 수행
    • reject()를 호출하면 catch가 수행
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Document</title>
    <script>
        // producer
        let promise = new Promise((resolve, reject) => {
            console.log('doing somthing...');
            // do some heavy work such as I/O
            setTimeout(() => {
                // resolve('sucess');
                reject(new Error("Network error"))
            }, 2000);
        });

        // consumer
        promise
            .then((value) => {
                console.log(value);
            })
            .catch(error => {
                console.log(error)
            })
            .finally(() => {
                console.log("finally")
            })
    </script>
</head>
<body>
</body>
</html>

Promise Chainning

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Document</title>
    <script>
        function job1() {
            return new Promise(function (resolve, reject) {
                setTimeout(() => {
                    resolve("job1 ok!");
                }, 2000);
            });
        }
        
        function job2() {
            return new Promise(function (resolve, reject) {
                setTimeout(() => {
                    resolve("job2 ok!");
                }, 2000);
            });
        }

        job1()
            .then((data)=> {
                console.log('data1',data);
                return job2()  //--> A
            }).catch((reason)=> {
                console.log('reason', reason)
                return Promise.reject(reason)
            }).then((data)=> { // <-- A
                console.log('data2',data);
		return job3()
            });
         
    </script>
</head>

<body> Hello </body>
</html>

Await

  • then then HELL!
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Document</title>
    <script>
        function timer(time) {
            return new Promise(function (resolve) {
                setTimeout(() => {
                    resolve(time);
                }, time);
            });
        }
				// then then Hell
        // console.log('start');
        // timer(1000)
        //     .then(time => {
        //         console.log('time:' + time);
        //         return timer(time + 1000)
        //     })
        //     .then(time=>{
        //         console.log('time:' + time);
        //         return timer(time + 1000)  
        //     })
        //     .then(time=>{
        //         console.log('time:' + time);
        //         return timer(time + 1000)  
        //     })
        //     .then(time=>{
        //         console.log('end');   
        //     });
            
            async function main() { 
                console.log('start');
                let time= await timer(1000)
                console.log('time:' + time);
                time = await timer(time + 1000) 
                console.log('time:' + time);
                time = await timer(time + 1000) 
                console.log('time:' + time);
                time = await timer(time + 1000) 
                console.log('time:' + time);                 
                console.log('end');
            }
            main()
    </script>
</head>

<body> Hello </body>
</html>

 

Create node.js Project

  • 프로젝트 폴더에서 npm init를 실행하여 Node.js의 프로젝트를 만들 수 있다.
    • npm init
    • pakage.json을 생성해서 하나의 프로젝트를 생성
Q : node.js를 설치했을때 글로벌 패키지는 어떤 파일에 저장되어 있나요?

A : Node.js에서 글로벌로 설치된 패키지는 일반적으로 시스템의 특정 디렉토리에 저장됩니다.

  • Windows에서는 글로벌 패키지가 다음 경로에 저장됩니다:
C:\Users\사용자이름\AppData\Roaming\npm\node_modules

 

Install Package Locally

npm install express

  • npm을 사용하여 설치된 모든 모듈은 node_modules 폴더 아래에 설치됩니다.

 

Uninstall Packages

  • 다음 명령은 애플리케이션에서 ExpressJS를 제거합니다.
npm uninstall express

node_modules 아래의 디렉터리들이 모두 제거되었다.

 

Add Dependency into package.json

  • 애플리케이션의 package.json에 종속성 항목을 추가합니다.
    • 하나의 프로젝트는 여러개의 라이브러리를 사용한다
    • 각 라이브러리도 버전이 여러가지가 있다.
    • 이 프로젝트에서 사용하는 라이브러리와 버전들을 package.json에서 관리할 필요가 있다.
      • npm5 부터는 --save 옵션 없이도 dependencies 항목에 자동적으로 추가되도록 업데이트
npm install express --save

 

정리

  • 패키지는 두가지 형태로 설치된다.
    • global (-g)
    • local
  • 전역적으로 설치하면
    • 각 프로젝트마다 설치할 필요가 없다.
    • Command Line에서 사용할 수 있다.
  • 지역적으로설치하면
    • 해당 프로젝트에만 적용된다.
    • 프로젝트마다 버전을 다르게 가져갈 수 있다.

package-lock

  • package.json에서는 버전정보를 저장할 때 version range를 사용
    • e.g.) "express": "~4.16.1"
  • 각 개발자가 npm i 를 사용해서 라이브러리를 설치하는 경우
    • 설치 시점에 따라 다른 버전이 설치될 수 있음
    • 버전 불일치로 문제 발생 가능하고 해결하기 번거로움
  • 이를 해결하기 위해 package-lock가 사용됨
    • 이 파일에는 정확한 버전 정보가 기록됨
    • 따라서, 다른 개발자에게 전달하거나, git에 업로드하는 경우 반드시 package-lock.json을 포함시킬 것

 

 

기본 웹 서버

웹 서버는 웹 응용 프로그램에 대한 모든 http 요청을 처리합니다.

Node.js는 생성 기능을 제공합니다.

  • 니 자신의 웹 서버는 HTTP 요청을 비동기적으로 다룰것입니다.

Node.js 웹 애플리케이션을 실행시키는 IIS 또는 Apache를 사용할 수 있다.

  • 하지만 Node.js 웹 서버를 사용하는 것을 추천한다.

 

Create Node.js 웹 서버

  • server.js
// 1 - Node.js 핵심 모듈을 import 한다.
var http = require('http'); 

// 2 - 서버 만들기
var server = http.createServer(function (req, res) {   

    //여기에서 들어오는 요청에 대해 다뤄라.
		// 비동기 이벤트 핸들러
});
server.listen(5000); //3 - 모든 들어오는 요청에 대해 듣는다.
console.log('Node.js web server at port 5000 is running..')

 

Handle HTTP Request

  • server.js
// Node.js의 핵심 모듈을 import 한다.
var http = require('http');

//웹 서버를 만든다.
var server = http.createServer(function(req, res) {
	if(req.url == '/') { //현재 요청의 URL을 체크한다.
    	// response 헤더를 둔다.
        res.write('<html><body><p>This is home Page.</p></body></html>');
        res.end();
    }
    else if (req.url == "/student") {
    	res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write('<html><body><p>This is admin Page.</p></body></html>');
        res.end();
    }
    else
    	res.end('Invalid Request!');
});


server.listen(5000); // 6 - 들어오는 요청들을 듣는다.
console.log('Node.js web server at port 5000 is running..')
  • 이제 아래와 같이 위의 웹 서버를 실행한다.
node server.js
  • 윈도우 사용자의 경우 브라우저에서 아래와 같이 입력
http://localhost:5000

 

JAVASCRIPT: Modules

  • 모듈을 사용하여 여러 개의 파일로 분할하는 좀더 개선된 방법
  • JavaScript 모듈을 사용하면 코드를 별도의 파일로 나눌 수 있습니다.
  • 이렇게 하면 코드를 유지하기가 더 쉬워진다.

모듈이란?

  • ES2015 모듈은 기본적으로 JavaScript 코드를 담고 있는 파일
    • script 태그에 type="module" 애트리뷰트를 추가해주면, 이 파일은 모듈로서 동작한다.
      • 파일 확장자로는 대개 mjs가 사용된다.
<script type="module" src="index.mjs"></script>
  • 다만 일반적인 JavaScript 파일과는 다른 여러가지 차이점을 갖고 있다.
    • import 혹은 export 구문을 사용할 수 있다.
    • 별다른 처리를 해주지 않아도 엄격 모드(strict mode)로 동작

모듈 스코프

  • 모듈 방식으로 분리하였을 경우의 장점
  • 모듈 내부의 가장 바깥 스코프에서 이름을 선언하더라도,
    • 전역 스코프가 아니라 모듈 스코프에서 선언
    • 따라서 여러 모듈의 가장 바깥쪽에서 같은 이름으로 변수, 함수, 클래스를 선언하더라도,
    • 서로 다른 스코프에서 선언되기 때문에 이름의 충돌이 생길 일이 없다.
  • 모듈 스코프에 선언된 이름은 (export 해주지 않는다면) 해당 모듈 내부에서만 접근

Export

  • 모듈의 기능을 외부에서 호출하게 하려면 Export해야 한다.
    • Import / Export
  • Export하지 않은 Identifier는 외부에서 접근 불가능
  • 함수 또는 변수를 내보낼 수 있다.
  • 두 개의 export 타입 : Named, Default

Named Exports

  • 두 가지 방식으로 named exports를 만들 수 있다.
    • In-line individually
    • all at once at the bottom
  • export는 한 개만 가능
    • 따라서 여러 개를 export하는 경우 객체 하나로 묶어서 전달
  • person.js
export const name = "Jesse";
export const age = 40;

// or

// const name = "Jesse";
// const age = 40;
// export {name, age};
  • index.html
    • import named exports from the file person.js
      • 필요한 것만 import 가능
<!DOCTYPE html>
<html>
<body>
<h1>JavaScript Modules</h1>

<p id="demo"></p>

<script type="module">
	import { name, age } from "./person.js";
    let text = "My name is " + name + ", I am " + age + ".";
    document.getElementById("demo").innerHTML = text;
</script>

</body>
</html>
  • 오류 확인 1 : 이름이 동일해야 한다. 
    • index.html에서 name을 fname으로 변경해보자.
  • 오류 확인 2 :
    • import를 하려면 <script> 보다는 <script type="module">를 사용해야 한다.