초기 자바스크립트는 느린언어로 서버에서 사용하기엔 부적절 했으나 구글이 c++
기반 V8엔진
을 만들면서 자바스크립트또한 v8엔진
을 통해 머신코드로 변경이 가능해졌고 속도가 빨라졌다.
https://engineering.huiseoul.com/자바스크립트는-어떻게-작동하는가-v8-엔진의-내부-최적화된-코드를-작성을-위한-다섯-가지-팁-6c6f9832c1d9
자바스크립트 속도가 빨라지자 웹 브라우저 이외에 공간에서도 사용되며 Nodejs
가 만들어졌다.
기존 톰캣 웹서버는 다중 스레드 기반의 동기방식의 요청을 지원한다.
동기방식이기 때문에 한 요청의 대한 처리(메서드)를 끝낼 때까지 기다려야 되고 사용자는 답답해진다.
따라서 사용자가 많아질 수록 요청을 처리할 스레드를 늘리게 되고 사용자의 요청또한 동기적으로 빠르게 처리되는 것이 스레드 기반 동기방식 네트워크 입출력이다.
Nodejs 는 단일스레드 이벤트 기반 비동기방식 네트워크 입출력을 지원한다.
비동기 방식이기 때문에 요청이 들어오면 요청에 대한 처리(워커스레드)를 실행만 해놓고 곧바로 다른 요청을 처리하러 간다.
워커스레드가 일을 끝내고 다끝냈음을 알리는 이벤트를 발생하면 그제서야 메인스레드가 해당 요청에 대한 결과값을 가져다 사용자에게 전달한다.
요청/반환 처리는 실제 하나의 메인 스레드가 모두 처리하지만 이벤트처리는 스레드풀의 여러 워커 스레드가 처리해준다.
하지만 이런 특성때문에 메인 스레드에서 에러발생시 서버가 종료되어 버린다.
효율적인 측면에서 이벤트가 발생할 때 마다 CPU 가 병목되는 톰캣보다는 Nodejs 가 좋다.
맥, 리눅스에서 npm을 사용해 라이브러리 설치시 발생하는 권한문제 해결방법
https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally
https://nodejs.org/ko/
위 주소로 이동해 안정버전이 LTS를 설치하자.
설치가 다 되었다면 테스트를 위해 다음과 같은 파일을 생성
node.basic.js
내용은 아래 한줄 작성
console.log("hello world");
그리고 해당 파일 위치에서 $ node node.basic.js
명령 실행하면 hello world가 출력되어야 한다.
JavaScript 를 사용한 프레임워크가 많아지면서 모듈화가 필요하게 되었다.
C언어에서 헤더파일을 정의하듯이 정의된 함수, 객체 등을 외부로 반출하고 싶을때 exports
키워드를 사용한다.
require
를 사용해 해당 반출된 정의 속성, 메서드를 사용할 수 있다.
// moudule.js
exports.abs = function (number) {
if (number > 0) {
return number;
} else {
return -number;
}
};
exports.circleArea = function (radius) {
return radius * radius * Math.PI;
}
abs
와 circleArea
메서드 정의 및 exports
// main.js
var module1 = require('./module.js');
console.log(module1.abs(-273)); // 273
console.log(module1.circleArea(3)); //274333882308138
모듈 생성시에는 exports
, 모듈 사용시에는 require
를 사용한다.
디렉토리 내의 js 파일을 index.js 로 설정시에 디렉토리 명으로만 require
가능하다.
아래와 같이 area
디렉토리 밑에 index.js
정의
.
├── area
│ └── index.js
├── main.js
├── package-lock.json
└── package.json
// index.js
let PI = Math.PI;
let area = (r) => PI * r * r;
module.exports = {
area: area,
pi: PI
}
// main.js
const foo = require('./area')
let area = foo.area(10);
console.log(area); // 314.1592653589793
console.log(foo.pi); // 3.141592653589793
ES6 부터 모듈링 시에 사용하는 키워드(exports
, require
도 사용 가능)
Nodejs
에산 최상위에 해당하는 여러 전역 변수와 기본 내장 객체, 전역 메서드, 변수가 존재한다.
global
, console
, process
, buffer
, require()
, __filename
, __dirname
, module
, exports
, Timeout
, setTimeout
, setInterval
, setImmediate
등
console
,setTimeout
,setInterval
,setImmediate
등의 객체 또는 메서드는global
객체에 정의되어 있으며global
을 통해 공용 전역변수 정의가 가능하다. 유지보수를 위해 남발하면 안됨.
주요 속성과 메서드
env
: 환경변수 정보argv
: 파라미터 정보exit()
: 프로세스 종료 메서드process.argv.forEach(function (item, index) {
console.log(index + ":" + typeof(item) + ":", item);
if (item == '--exit') {
var exitTime = Number(process.argv[index+1]);
setTimeout(function () {
process.exit();
}, exitTime);
}
});
0:string: /usr/local/bin/node
1:string: /Users/gojiyong/Documents/nodejs/node.process.js
2:string: --exit
3:string: 10000
console.log("- process.evn : ", process.evn);
console.log("- process.version : ", process.version);
console.log("- process.versions : ", process.versions);
console.log("- process.arch : ", process.arch);
console.log("- process.platform : ", process.platform);
console.log("- process.connected : ", process.connected);
console.log("- process.execArgv : ", process.execArgv);
console.log("- process.exitCode : ", process.exitCode);
console.log("- process.mainModule : ", process.mainModule);
console.log("- process.release : ", process.release);
console.log("- process.memoryUsage() : ", process.memoryUsage());
console.log("- process.uptime() : ", process.uptime());
console.log("- process.uptime() : ", process.uptime());
console.log("- process.uptime() : ", process.uptime());
- process.evn : undefined
- process.version : v10.16.1
- process.versions : { http_parser: '2.8.0',
node: '10.16.1',
v8: '6.8.275.32-node.54',
uv: '1.28.0',
zlib: '1.2.11',
brotli: '1.0.7',
ares: '1.15.0',
modules: '64',
nghttp2: '1.34.0',
napi: '4',
openssl: '1.1.1c',
icu: '64.2',
unicode: '12.1',
cldr: '35.1',
tz: '2019a' }
- process.arch : x64
- process.platform : darwin
- process.connected : undefined
- process.execArgv : []
- process.exitCode : undefined
- process.mainModule : Module {
id: '.',
exports: {},
parent: null,
filename: '/Users/gojiyong/Documents/nodejs/node.process2.js',
loaded: false,
children: [],
paths:
[ '/Users/gojiyong/Documents/nodejs/node_modules',
'/Users/gojiyong/Documents/node_modules',
'/Users/gojiyong/node_modules',
'/Users/node_modules',
'/node_modules' ] }
- process.release : { name: 'node',
lts: 'Dubnium',
sourceUrl:
'https://nodejs.org/download/release/v10.16.1/node-v10.16.1.tar.gz',
headersUrl:
'https://nodejs.org/download/release/v10.16.1/node-v10.16.1-headers.tar.gz' }
- process.memoryUsage() : { rss: 26419200,
heapTotal: 7061504,
heapUsed: 4256752,
external: 8272 }
- process.uptime() : 0.156
https://nodejs.org/dist/latest-v10.x/docs/api/os.html
const os = require('os')
console.log(os.hostname());
console.log(os.type());
console.log(os.platform());
console.log(os.arch());
console.log(os.release());
console.log(os.uptime());
console.log(os.loadavg());
console.log(os.totalmem());
console.log(os.freemem());
console.log(os.cpus());
console.log(os.networkInterfaces());
https://nodejs.org/dist/latest-v10.x/docs/api/fs.html
const http = require('http');
const fs = require('fs');
const url = require('url')
const userList = [
{ name: 'ko', age: 25 },
{ name: 'ho', age: 24 },
{ name: 'jo', age: 27 },
]
fs.writeFileSync('./list.json', JSON.stringify(userList), (err) => {
if (err) throw err;
console.log('write success');
})
// 생성된 `list.json` 데이터
// [{"name":"ko","age":25},{"name":"ho","age":24},{"name":"jo","age":27}]
fs.readFile('./list.json', (err, data) => {
if (err) throw err;
const json = JSON.parse(data);
console.log(json[0].name); // ko
console.log(json[1].name); // ho
console.log(json[2].name); // jo
})
https://nodejs.org/dist/latest-v10.x/docs/api/http.html
아래처럼 request(IncomingMessage)
, response(ServerResponse)
, fs
모듈을 사용해 html
데이터 반환
const server = http.createServer((request, response) => {
let pahtName = url.parse(request.url).pathName;
if (pathName === '/') {
fs.readFile('./136.index.html', (err, data) => {
response.writeHead(200, { 'Content-type': 'text/html' });
response.end(data);
console.log(url.parse(request.url));
})
} else if (pathName === './example') {
fs.readFile('./136.example.html', (err, data) => {
response.writeHead(200, { 'Content-type': 'text/html' });
response.end(data);
console.log(url.parse(request.url));
})
}
}).listen(5000, () => {
console.log('server is running, http://localhost:5000');
});
const http = require('http');
const server = http.createServer().listen(5000, () => {
console.log('server is running, http://localhost:5000');
});
// client request 시 console 에 출력
server.on('request', () => {
console.log('request')
})
클라이언트 request
시에 설정한 콜백함수 (이벤트) 발생, request
외에 아래와 같은 이벤트가 있음.
request
- 클라이언트 요청시 발생하는 이벤트connection
- 클라이언트 접속시 발생하는 이벤트close
- 서버가 종료시 발생하는 이벤트clientError
- 클라이언트 오류시 발생하는 이벤트checkContinue
- 클라이언트 지속연결시 발생하는 이벤트const http = require('http');
const server = http.createServer((request, response) => {
response.writeHead(200, {
'Content-Type': 'text/html',
'Set-Cookie': ['soju=grill', 'beer=chicken']
});
response.end(`<h1>${request.headers.cookie}</h1>`)
}).listen(5000, () => {
console.log('server is running, http://localhost:5000');
});
request.headers.cookie
속성에서 가져올 수 있으며 아래와 같은 string
type
의 데이터가 저장된다.
soju=grill; beer=chicken
split
과 map
을 사용해 객체로 추출 가능
const cookie = request.headers.cookie.split(';').map((element) => {
element = element.split('=');
return { name: element[0], value: element[1] };
});
response.end(`<h1>${JSON.stringify(cookie)}</h1>`)
출력값
[{"name":"soju","value":"grill"},{"name":" beer","value":"chicken"}]
https://nodejs.org/dist/latest-v10.x/docs/api/url.html
url.parse(urlStr, [parseQueryString], [slashesDenoteHost])
urlStr
문자열을 아래 Url
객체로 반환
interface Url {
auth: string | null;
hash: string | null;
host: string | null;
hostname: string | null;
href: string;
path: string | null;
pathname: string | null;
protocol: string | null;
search: string | null;
slashes: boolean | null;
port: string | null;
query: string | null | ParsedUrlQuery;
}
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ href │
├──────────┬──┬─────────────────────┬────────────────────────┬───────────────────────────┬───────┤
│ protocol │ │ auth │ host │ path │ hash │
│ │ │ ├─────────────────┬──────┼──────────┬────────────────┤ │
│ │ │ │ hostname │ port │ pathname │ search │ │
│ │ │ │ │ │ ├─┬──────────────┤ │
│ │ │ │ │ │ │ │ query │ │
" https: // user : pass @ sub.example.com : 8080 /p/a/t/h ? query=string #hash "
│ │ │ │ │ hostname │ port │ │ │ │
│ │ │ │ ├─────────────────┴──────┤ │ │ │
│ protocol │ │ username │ password │ host │ │ │ │
├──────────┴──┼──────────┴──────────┼────────────────────────┤ │ │ │
│ origin │ │ origin │ pathname │ search │ hash │
├─────────────┴─────────────────────┴────────────────────────┴──────────┴────────────────┴───────┤
│ href │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(All spaces in the "" line should be ignored. They are purely for formatting.)
parseQueryString(boolean)
: true
지정시 반환타입이 UrlWithParsedQuery
, 기본값 false
interface UrlWithParsedQuery extends Url {
query: ParsedUrlQuery; // dict 형식의 데이터
}
slashesDenoteHost(boolean)
: true
지정시 //
뒤의 값을 host
, 그 뒤의 값을 path
로 인식. http://
가 붙으면 true
, false
상관 없이 자동으로 파싱하기에 별도 설정할 필요 없음. 기본값 false
const http = require('http');
const fs = require('fs');
const url = require('url')
const get1 = url.parse('//127.0.0.1:5000/path?test=1234#hash', true, true);
const get2 = url.parse('//127.0.0.1:5000/path?test=1234#hash', true, false);
console.log(get1.host, get1.path)
console.log(get2.host, get.path)
출력값
127.0.0.1:5000 /path?test=1234
null '/path?test=1234'
supervisor
supervisor
는 개발시 유용한 도구로 파일이 수정되면 알아서 서버를 재실행해준다.
사용법은 node
와 똑같이 뒤에 파일명을 지정하면 됨.
$ npm install -g supervisor
$ supervisor <파일명>
forever
forever
백그라운드 서버 실행 툴로 서버를 뒤에서 관리할 수 있음.
크게 start
, restart
, list
, stop
명령어가 있음.
$ npm install -g forever
$ forever start app.js
warn: --minUptime not set. Defaulting to: 1000ms
warn: --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info: Forever processing file: app.js
$ forever restart 0
info: Forever restarted process(es):
data: uid command script forever pid id logfile uptime
data: [0] b9ZY /usr/local/bin/node app.js 16189 16196 /Users/gojiyong/.forever/b9ZY.log 0:0:0:5
$ forever list
info: Forever processes running
data: uid command script forever pid id logfile uptime
data: [0] b9ZY /usr/local/bin/node app.js 16189 16203 /Users/gojiyong/.forever/b9ZY.log 0:0:0:3.847
$ forever stop 0
info: Forever stopped process:
uid command script forever pid id logfile uptime
[0] b9ZY /usr/local/bin/node app.js 16189 16203 /Users/gojiyong/.forever/b9ZY.log 0:0:0:7.741
맨앞의 숫자 0
외에도 uid
, script
로도 restart
, stop
명령 실행 가능
pm2
forever
와 비슷하게 백그라운드 실행, 관리툴
$ pm2 start app.js
[PM2] Applying action restartProcessId on app [app](ids: 0)
[PM2] [app](0) ✓
[PM2] Process successfully started
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name │ mode │ ↺ │ status │ cpu │ memory │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0 │ app │ fork │ 1 │ online │ 0% │ 7.1mb │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
$ pm2 stop app
[PM2] Applying action stopProcessId on app [app](ids: 0)
[PM2] [app](0) ✓
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name │ mode │ ↺ │ status │ cpu │ memory │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0 │ app │ fork │ 1 │ stopped │ 0% │ 0b │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
java 의 stream 같은 메서드
http://kbs0327.github.io/blog/technology/lodash/