Node.js
Node.js 소개
Node.js는 비동기 이벤트 주도 JavaScript 런타임으로써 확장성 있는 네트워크 애플리케이션을 만들 수 있도록 설계되었다.
https://nodejs.org/ko/about/Node.js의 특징
비동기 I/O 처리 / 이벤트 위주
Node.js 라이브러리의 모든 API는 비동기식(Non-blocking).
Node.js 기반 서버는 API가 실행되었을때, 데이터를 반환할때까지 기다리지 않고 다음 API 를 실행합니다. 그리고 이전에 실행했던 API가 결과값을 반환할 시, NodeJS의 이벤트 알림 메커니즘을 통해 결과값을 받아옵니다.빠른 속도
구글 크롬 V8 자바스크립트 엔진을 사용하여 빠른 코드 실행을 제공단일 쓰레드 / 뛰어난 확장성
Node.js는 이벤트 루프와 함께 단일 쓰레드 모델을 사용.
이벤트 메커니즘은 서버가 멈추지않고 반응하도록 해주어 서버의 확장성을 키워줍니다. 반면, 일반적인 웹서버는 (Apache) 요청을 처리하기 위하여 제한된 쓰레드를 생성합니다. Node.js 는 쓰레드를 한개만 사용하고 Apache 같은 웹서버보다 훨씬 많은 요청을 처리할 수 있습니다.노 버퍼링
Node.js application엔 데이터 버퍼링이 없고, 데이터를 chunk로 출력라이센스
Node.js 는 MIT License가 적용되어있습니다.
Node.js가 사용되면 효율적인 분야
- 입출력이 잦은 어플리케이션
- 데이터 스트리밍 어플리케이션
- 데이터를 실시간으로 다루는 어플리케이션
- JSON API 기반 어플리케이션
- 싱글페이지 어플리케이션
Node.js 비효율적인 분야
- cpu 사용률이 높은 어플리케이션
Node.js 설치
https://nodejs.org/ko 에서 LTS 버젼 설치

LTS(Long Term Supported)버젼은 장기적으로 안정된지원이 보장
Current버젼은 최신기능을 제공하지만 지속적인 업데이트로 안정적이지 않을수 있다.
node.js 설치시 npm도 같이 설치되며 CMD(명령 프롬프트)에서 설치를 확인할수 있다.
> node -v
v12.2.0
> npm -v
6.9.0
NPM(Node Package Manager)
Node.js에서 사용할 수 있는 모듈들을 패키지화하여 모아둔 저장소 역할과 패키지 설치 및 관리를 위한 CLI(Command line interface)를 제공한다.
자신이 작성한 패키지를 공개할 수도 있고 필요한 패키지를 검색하여 재사용할 수도 있다.
> npm intall <-package>
> npm i <-package>
install 명령어중 별도의 옵션을 지정하지않으면 지역(local)으로 설치되며, -g 옵션을 지정하면 전역(global)으로 설치된다.
> npm i -g <-package>
package.json과 의존성 관리
node.js 프로젝트에서는 많은 패키지를 사용하게 되고 패키지의 버젼도 빈번하게 업데이트 되므로 프로젝트가 의존하고 있는 패키지를 일괄 관리 할 필요가 있다.
npm은 packge.json 파일을 통해서 프로젝트 정보와 패키지의 의존성(dependency)을 관리한다.
이미 작성된 package.json이 있다면 팀 내에 배포하여 동일한 개발 환경을 빠르게 구축할수 있는 장점이 있다.
package.json은 Java의 maven에서 pom.xml과 비슷한 역할을 한다.
devDependencies에는 개발 시에만 사용하는 개발용 의존 패키지를 명시한다.
예를 들어 TypeScript와 같은 트랜스파일러는 개발 단계에서만 필요하고 배포할 필요는 없으므로 devDependencies에 포함시킨다.
npm install 명령어에 --save-dev(축약형 -D) 옵션을 사용하면 패키지 설치와 함께 package.json의 devDependencies에 설치된 패키지와 버전이 기록된다.
> npm i -D <-package>
// package.json
"devDependencies": {
"-package": "@version"
}
자주 사용하는 npm 명령어
package.json 생성
> npm init
// 기본설정
> npm init -y
패키지 설치
//로컬 설치
> npm install <-package>
// 전역 설치
> npm install -g <-package>
// 개발 설치
> npm install -D <-package>
패키지 제거
// 로컬/개발 패키지 제거
> npm uninstall <-package>
// 전역 패키지 제거
> npm uninstall -g -
패키지 업데이트
> npm update <-package>
package.json scripts 프로퍼티의 start 실행
> npm start
package.json scripts 프로퍼티의 start 이외의 scripts 실행
> npm run <-script-name>
전역 패키지 설치 폴더 확인
> npm root -g
패키지 정보 참조
> npm view <-script-name>
Node.js Application 만들기
1단계 필요한 모듈 import하기
어플리케이션에 필요한 모듈을 불러올땐 require 명령을 사용합니다.
다음 코드는 HTTP 모듈을불러오고 반환되는 HTTP 인스턴스를 http 변수에 저장합니다.
var http = require("http");
2단계 서버 생성하기
1단계에서 만든 http 인스턴스를 사용하여 http.createServer() 메서드를 실행
listen 메서드를 사용하여 포트8081과 bind 해준다.
http.createServer()는 request와 response를 매개변수로 함수가 실행된다.
http.createServer(function(request, response){
/*
HTTP 헤더 전송
HTTP Status: 200 : OK
Content Type: text/plain
*/
response.writeHead(200, {'Content-Type': 'text/plain'});
/*
Response Body 를 "Hello World" 로 설정
*/
response.end("Hello World\n");
}).listen(8081);
3단계 서버테스트 해보기
위 1,2 단계로 작성된 main.js를 실행
> node main.js

callback function
javascript에서 function은 일급 객체. 즉, 함수는 Object타입이며 다른 일급 객체(string, arrary, number 등)와 똑같이 사용될수 있다.
function 자체가 객체이므로 변수 안에 담을 수 도 있고, 인수로써 다른 함수에 전달 해 줄수도 있고, 함수에서 만들어 질수도 있고, 반환될수도 있다.
callback function은, 특정함수에 매개변수로써 전달된 함수를 지칭하고, 전달 받은 함수 안에서 호출된다.
jQuery에서 사용된 callback function 예제
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
// click 메소드에 이름이 없는 callback function을 인수로 전달
// jQuery 안의 click 메소드에서는, 마우스 클릭이 있으면 callback function을 호출
click 메소드의 인수가 함수. 이함수가 callback function.
callback function이 많이 사용되는 이유
callback function이 사용되지않은, blocking code 예제
// input.txt
Let's understand what is a callback function.
What the HELL is it?
// main.js
var fs = require("fs");
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("Program has ended");
// node main.js
Let's understand what is a callback function.
What the HELL is it?
Program has ended
Callback function이 사용된 Non-Blocking Code 예제
blocking code예제와 다르게 function이 실행될때 종료를 기다리지 않고 다음 코드를 실행하고
function에 있던 작업이 끝나면 callback function을 호출한다.
// main.js 수정
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("Program has ended");
// node main.js
Program has ended
Let's understand what is a callback function.
What the HELL is it?
모든 node application의 비동기식 함수에서는 첫번째 매개변수로는 error를 마지막 매개변수로는 callback function을 받는다.
fs.readFile() 함수는 비동기식으로 파일을 읽는 함수이고, 도중에 error가 발생하면 err객체에 내용을 담고, 정상흐름이면 파일내용을 읽고 출력.
readFile() 메서드가 실행 된후, 프로그램이 메소드가 끝날때까지 대기하지않고, 곧바로 다음 명령어를 진행 하였기 때문에, "Program has ended"가 먼저 출력된다.
callback function을 사용하여 이렇게 프로그램의 흐름을 끊지 않음으로서, Non-Blocking 코드를 사용하는 서버는 Blocking 코드를 사용하는 서버보다 더 많은 양의 요청을 빠르게 처리 할 수 있게된다.
Javascript engine

Event Loop와 비동기 동작
event loop 강의 : https://www.youtube.com/watch?v=gP0J1WZZRr4&t=212s
싱글 쓰레드로 작동하는 자바스크립트에서 비동기적인 프로그래밍을 위한 장치로 call stack queue을 주기적으로 감시(폴링)하고 있다가 비어있는것이 체크되면 callback queue의 최상단의 코드작업을 call stack queue으로 이동시킨다.
call stack
stack 구조를 통해 함수가 순차적으로 실행될때 순서처리를 기록. 함수가 시작되면 callstack에 해당 함수가 쌓이고(push), 함수가 종료되면 callstack에서 제거된다(pop). 만약 함수가 종료되기 전에 또 다른 함수가 실행되면, 해당함수는 종료되지 않은 기존 함수 스택위에 새로운 스택으로서 쌓이게 된다.

callback queue
비동기적으로 실행된 callback function들이 보관되는 영역이다. Promise는 Microtask Queue로, Timeout은 Task Queue로, RequestAnimationFrame은 Animation Frame으로 콜백함수를 밀어넣는다. 이후 event loop의 callback 이동 우선순위는 크롬기준 Microtask Queue > Animation Frames > Task Queue 이다.

web apis
node.js와 web browser의 차이. client에 반응 하는 event를 처리하는 코드가 web api queue에서 별도로 작업한다. web apis는 web browser를 기반으로 하는 api이기 때문에 node.js의 api와 다르다.
queue 자료구조
데이터순서가 처음부터 끝이 있다면 앞에 입력된 데이터를 먼저 출력하는 자료구조.
var, let, const
차이점.
javascript의 변수 var
의 문제점.
// 이미 만들어진 변수이름으로 재선언했는데 아무런 문제가 발생하지 않는다.
var a = 'test'
var a = 'test2'
// hoisting으로 인해 ReferenceError에러가 안난다.
c = 'test'
var c
위 같은 문제점으로 ES2015에서 추가된 let, const
는 둘다 변수 재선언이 불가능하며 let
은 변수에 재할당이 가능하지만, const
는 변수 재할당이 불가능하다.
node.js 모듈
자바스크립트는 웹페이지에 있어서 보조적인 기능을 수행하기 위해 한정적인 용도로 만들어진 태생적 한계로 다른 언어에 비해 부족한(나쁜) 부분이 있는 것이 사실이다. 그 대표적인 것이 모듈 기능이 없는 것이다.
브라우저 상에서 동작하는 JavaScript는 script tag로 로드하며 복수의 JavaScript 파일을 로드할 경우 하나의 파일로 merge되며 동일한 유효범위를 갖게 된다.
ES6에서는 Client-side JavaScript에서도 동작하는 모듈 기능을 추가하였다. 단 현재 대부분의 브라우저가 ES6의 모듈을 지원하지 않고 있으므로 ES6 모듈을 현재의 브라우저에서 사용하기 위해서는 SystemJS, RequireJS 등의 모듈 로더 또는 Webpack 등의 모듈번들러를 사용하여야 한다. ES6 모듈은 키워드 export, import를 제공한다.
JavaScript를 Client-side에 국한하지 않고 범용적으로 사용하고자 하는 움직임이 생기면서 모듈 기능은 반드시 해결해야하는 핵심 과제가 되었고 이런 상황에서 제안된 것이 CommonJS와 AMD(Asynchronous Module Definition)이다.
Node.js는 사실상 모듈 시스템의 사실상 표준(de facto standard)인 CommonJS를 채택하였고 현재는 독자적인 진화를 거쳐 CommonJS 사양과 100% 동일하지는 않지만 기본적으로 CommonJS 방식을 따르고 있다.
Node.js는 module 단위로 각 기능을 분할할 수 있다. module은 파일과 1대1의 대응 관계를 가지며 하나의 모듈은 자신만의 독립적인 실행 영역(Scope)를 가지게 된다. 따라서 클라이언트 사이드 JavaScript와는 달리 전역변수의 중복 문제가 발생하지 않는다.
모듈은 module.exports 또는 exports 객체를 통해 정의하고 외부로 공개한다. 그리고 공개된 모듈은 require 함수를 사용하여 임포트한다.
exports
모듈은 독립적인 파일 스코프를 갖기 때문에 모듈 안에 선언한 모든 것들은 기본적으로 해당 모듈 내부에서만 참조 가능하다. 만약 모듈 안에 선언한 항목을 외부에 공개하여 다른 모듈들이 사용할 수 있게 하고 싶다면 exports 객체를 사용해야 한다.
모듈을 파일로 작성하고 외부에 공개할 대상을 exports 객체의 프로퍼티 또는 메소드를 정의한다. 그리고 모듈을 전역 함수 require()를 이용하여 추출한다.
// circle.js
// circle 모듈안에서만 유효
const { PI } = Math;
exports.area = (r) => PI * r * r;
exports.circumference = (r) => 2 * PI * r;
circle.js는 독립적인 파일 스코프를 갖는 모듈이다. circle 모듈에서 area와 circumference를 exports 객체의 메소드로 정의하였다. 변수 PI는 circle 모듈에서만 유효한 private 변수가 되고, area와 circumference는 외부에 공개된다.
// app.js
const circle = require('./circle.js'); // == require('./circle')
console.log(`지름이 4인 원의 면적: ${circle.area(4)}`);
console.log(`지름이 4인 원의 둘레: ${circle.circumference(4)}`);
require 함수를 사용하여 임의의 이름으로 circle 모듈을 import한다. 모듈의 확장자는 생략할 수 있다.
이때 circle 모듈은 객체로 반환된다. 따라서 circle.area, circle.circumference와 같은 형식으로 공개된 circle 모듈을 참조한다. app.js를 실행해 보자.
> node app
지름이 4인 원의 면적: 50.26548245743669
지름이 4인 원의 둘레: 25.132741228718345
module.exorts
exports 객체는 프로퍼티 또는 메소드를 여러개 정의할수 있었다. 하지만 module.exports에는 하나의 값(원시 타입, 함수, 객체)을 할당할 수 있다.
// circle.js
const { PI } = Math;
module.exports = function (r) {
return {
area() { return PI * r * r; },
circumference() { return 2 * PI * r}
};
}
circle 모듈의 module.exports에는 하나의 함수를 할당하였다.
// app.js
const circle = require('./circle');
const myCircle = circle(4);
console.log(`지름이 4인 원의 면적: ${myCircle.area()}`);
console.log(`지름이 4인 원의 둘레: ${myCircle.circumference()}`);
require 함수를 통해 circle 모듈을 임포트하여 circle 변수에 할당하였다. 이때 circle 변수는 circle 모듈에서 module.exports에 할당한 값 자체 즉 객체를 반환하는 함수이다.
// primitive.js
const pv = 'primitive value';
module.exports = pv;
함수 뿐만아니라 원시값도 할당할수 있다.
// app.js
const value = require('./primitive');
console.log(value); // => 'primitive value'
exports와 module.exports는 혼동하기 쉽다. exports는 module.exports에의 참조이며 module.exports의 alias이다. 즉, exports는 module.exports와 같다고 보아도 무방하다.
구분 | 모듈 정의 방식 | require 함수의 호출결과 |
---|---|---|
exports | exports 객체에는 값을 할당할 수 없고 공개할 대상을 exports 객체에 프로퍼티 또는 메소드로 추가한다. | exports 객체에 추가한 프로퍼티와 메소드가 담긴 객체가 전달된다. |
module.exports | module.exports 객체에 하나의 값(원시 타입, 함수, 객체)만을 할당한다. | module.exports 객체에 할당한 값이 전달된다. |
module.exports에 함수를 할당하는 방식
// foo.js
modele.exports = function(a, b) {
return a + b;
}
// app.js
const add = require('./foo')
const result = add(1, 2);
console.log(result); // => 3
module.exports는 1개의 값만을 할당할 수 있다. 모듈에서 1개의 값만을 공개하는 것은 불편할 수 있다.
다음과 같이 객체를 사용하여 복수의 기능을 하나로 묶어 공개하는 방식을 사용할 수 있다.
exports에 객체를 할당하는 방식
// foo.js
module.exports = {
add(v1, v2) { return v1 + v2 },
minus (v1, v2) { return v1 - v2 }
};
// app.js
const calc = require('./foo')
const result1 = calc.add(1, 2);
console.log(result1); // => 3
const result2 = calc.minus(1, 2);
console.log(result2); // => -1
require
project/
├── app.js
└── module/
├── index.js
├── calc.js
└── print.js
아래과 같이 모듈을 명시하지 않고 require 함수를 호출하면 해당 디렉터리의 index.js을 로드한다.
const myModule = require('./module');
이때 로드되는 index.js 내에서 calc.js과 print.js를 require하면 한번의 require로 alc.js과 print.js의 모든 기능을 사용할 수 있다.
// module/index.js
module.exports = {
calc: require('./calc'),
print: require('./print')
};
// module/calc.js
module.exports = {
add (v1, v2) { return v1 + v2 },
minus (v1, v2) { return v1 - v2 }
};
// module/print.js
module.exports = {
sayHello() { console.log('Hi!') }
};
// app.js
const myModule = require('./module');
// module/calc.js의 기능
const result = myModule.calc.add(1, 2);
console.log(result);
// module/print.js의 기능
myModule.print.sayHello();
코어 모듈과 파일 모듈
Node.js는 기본으로 포함하고 있는 모듈이 있다. 이를 코어 모듈이라 한다. 코어 모듈을 로딩할 때에는 패스를 명시하지 않아도 무방하다.
const http = require('http');
npm을 통해 설치한 외부 패키지 또한 패스를 명시하지 않아도 무방하다.
const mongoose = require('mongoose');
코어 모듈과 외부 패키지 이외는 모두 파일 모듈이다. 파일 모듈을 로딩할 때에는 패스를 명시하여야 한다.
const foo = require('./lib/foo');