Node.js

Node.js - 수업 2일차

kakaroo 2022. 8. 23. 11:50
반응형

 

자바스크립트는 태생적으로 웹 브라우저 위에서만 실행할 수 있었다. 웹 브라우저는 자바스크립트 런타임을 내장하고 있으므로 자바스크립트 코드를 실행할 수 있다. 브라우저 외의 환경에서 자바스크립트를 실행하기 위한 여러 시도가 있었으나 자바스크립트의 실행속도 문제 때문에 큰 반향을 얻지는 못했다.

2008년 구글이 자바스크립트 실행을 위한 vs 엔진을 크롬이 출시하면서 속도 문제가 해결이 되었다.

라이언 달(Ryan Dahl)이라는 사람이 2009년 vs 엔진 기반의 노드 프로젝트를 시작했고, 이것이 바로 Node.js 이다.

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

 

Node.js는 확장성 있는 네트워크 애플리케이션(특히 서버 사이드) 개발에 사용되는 소프트웨어 플랫폼이다. 작성 언어로 자바스크립트를 활용하며 논블로킹(Non-blocking)[3] I/O와 단일 스레드 이벤트 루프를 통한 높은 처리 성능을 가지고 있다.

내장 HTTP 서버 라이브러리를 포함하고 있어 웹 서버에서 아파치 등의 별도의 소프트웨어 없이 동작하는 것이 가능하며 이를 통해 웹 서버의 동작에 있어 더 많은 통제를 가능케 한다.

 

<Node.js의 특징>

1. 자바 스크립트 언어 사용

2. 싱글 쓰레드

3. 이벤트 기반

4. 비동기 I/O

-> 비동기 I/O와 싱글 쓰레드 기반의 이벤트 처리를 사용하여 높은 성능을 제공한다.

 

동기 : 결과값 반환을 무한정 기다린다.

비동기 : 결과값을 마냥 기다리지 않고 완료되면 호출되는 콜백으로 대체한다.

 

Node.js 에서 자바스크립트 실행방법

1. node 실행 -> REPL 모드로 변경되어 바로 입력하면 됨

 

2. node 파일명.js 실행  (확장자 생략 가능)

 

//0_hello.js

console.log("Hello world");

 

REPL
Read : 사용자가 입력한 코드를 읽음
Evaluate : 읽은 코드를 실행(평가)
Print : 실행(평가)된 결과를 출력
Loop : 위의 순서를 반복

브라우저에서 콘솔창(F12)이나 Node 실행시 리플이 실행됨

주요 단축기
REPL 종료 : Ctrl + C 2번 또는 Ctrl + D
TAB : 일부 입력된 값으로 시작하는 명령어 또는 변수 목록을 확인
.save 파일명 : 현재 REPL 세션을 파일로 저장
.load 파일명 : 파일로 저장된 REPL 세션을 불러옴

 

<Sync vs ASync>

//1_sync.js

//어떤 일을 수행하고 있다는 것을 확인하기 위해 1초마다 점을 출력하는 함수
const doSomething = () => {
	const process = require("process");
	setInterval(() => {
		process.stdout.write(".");
	}, 1000);
}

//doSomething();

//다운로드를 동기적으로 수행하는 함수
const downloadSync = (url) => {
	console.log("download start");
	let endTime = new Date(new Date().getTime() + 5000);
	while (endTime >= new Date()) {}
	console.log("download complete!");
}
//downloadSync("xyz.com/hello.mp3");
//doSomething();	//download가 다 수행된 다음에 해당 함수가 실행된다.

//다운로드를 비동기적으로 수행하는 함수
//download가 수행되면서 doSomething 도 같이 수행된다.
const download = (url) => {
	console.log("download start");
	setTimeout(() => { console.log("download complete!");}, 5000);
}
download("xyz.com/hello.mp3");
doSomething();

 

//2_callback.js

//동기식 함수 : 결과값이 리턴된다.
const accumulateSync = (start, end) => {
	let cumsum = 0;
	for(let n=start; n<=end; n++) {
		cumsum += n;
	}
	return cumsum;
}

console.log(accumulateSync(1, 1000000));

//비동기식 함수
const accumulate = (start, end, callback) => {
	let cumsum = 0;
	for(let n=start; n<=end; n++) {
		cumsum += n;
	}
	callback(cumsum);	//결과를 콜백함수로 전달, 오류처리를 해야 하지만 일단 skip
}

accumulate(1, 1000000, result => console.log(result));

 

//3_callback2.js

const fs = require("fs");	//파일관련 처리를 모아둔 것

//동기식 함수의 사용
//Node.js 에서는 동기식 함수에 대하여 주로 Sync라는 접미사를 사용
const fileNames = fs.readdirSync(".");
for(const fname of fileNames) {
	console.log(fname);
}

console.log("--------------");

//비동기식 함수의 사용
fs.readdir(".", (err, fileNames) => {
	for(const fname of fileNames) {
		console.log(fname);
	}
});

 

<Object>

//4_object.js

//Node.js에서는 별도의 로딩 없이 바로 사용가능한 객체들을 제공하는데
//이를 전역 객체라고 한다.

//1. global 객체 : 브라우저에서 window 객체의 개념과 유사
//global 객체는 다양한 객체를 속성으로 가지고 있으며 대표적인 예가 console 이다.
global.console.log("hello world");	//window.document

//global 객체의 속성은 global 없이 사용할 수 있다.
console.log("hello world");	//document

//2. process: 현재 프로세스의 실행정보를 가지고 있는 전역객체
//console과 마찬가지로 global 객체의 속성으로 존재
console.log(process.env);	//환경정보
console.log(process.arch);	//CPU 아키텍처 정보
console.log(process.platform);	//플랫폼 정보
console.log(process.uptime());	//Node.js 실행시간
console.log(process.version);	//Node.js 버전정보
console.log(process.pid);	//프로세스 아이디
//console.log(process.argv);	//명령행의 인자 정보

//명령행에 node add.js 10 20 라고 입력할 경우 각 토큰은 배열에 저장되어 전달한다
//			0   1     2  3

const argv = process.argv
for(let i=0; i<argv.length; i++) {
	console.log(`argv[${i}] = ${argv[i]}`);
}
/* node 4_object.js 10 20 수행결과
argv[0] = /usr/bin/node
argv[1] = /workspace/SWEDU_nodejs_mysql3/20220823/4_object.js
argv[2] = 10
argv[3] = 20
*/

//명령행으로부터 들어온 값은 문자열이므로 수치연산을 수행하려면 수치데이터로 변환해야 한다.
const n1 = parseInt(argv[2]);
const n2 = parseInt(argv[3]);
console.log(n1+n2);

//현재 프로세스를 종료하고 싶다면 exit 메서드를 사용하면 된다.
//process.exit();
//console.log("end");	//화면에 출력되지 않는다.

//3. console
//로그 레벨을 지정하여 출력할 수 있다.
console.log("로그 출력");
console.info("정보 출력");
console.warn("경고 출력");
console.error("오류 출력");

//표준 입출력: 컴퓨터에서 기본으로 사용하는 입출력 스트림
//표준 입력: 키보드(버퍼)
//표준 출력: 모니터(버퍼)
//표준 오류: 모니터

//info와 log는 표준출력으로 전송하고 warn과 error는 표준오류로 전송한다.
const value = 3.14;
console.log("value: "+value);	//덧셈 연산자 사용
console.log("value: ",value);

//객체 출력시 덧셈 연산자보다는 쉼표를 사용하는 것이 좋다.
const obj = { name:"kakaroo", age:20 };
console.log("obj: "+obj);	//obj: [object Object]
console.log("obj: ",obj);	//obj: { name: 'kakaroo', age: 20 }


//실행시간 축정
//console.time(타이머이름): 시작시점 설정
//console.timeEnd(동일 타이머 이름) : 종료시점까지 걸린시간을 계산해서 출력
const accumulateSync = (start, end) => {
	let cumsum = 0;
	for(let n=start; n<=end; n++) {
		cumsum += n;
	}
	return cumsum;
}
console.time("MY_TIMER");
console.log(accumulateSync(1, 1000000));
console.timeEnd("MY_TIMER");	//MY_TIMER: 6.800ms


//4. 현재 경로 출력하기
console.log(__filename);
console.log(__dirname);

 

<Module>

//5_module.js

//모듈이란 응용프로그램을 구성하는 개별적 요소
//파일 단위로 이루어져 있으며 필요에 따라 명시적으로 불러올 수 있다.

//1. 기본모듈: Node.js와 함께 설치되는 모듈로 별도의 추가설치가 필요없음
//2. 확장모듈: 기본 모듈 이외의 모듈로 npm(node pakcage manager)

//Node.js에서 제공하는 기본 모듈
// - 프로세스 및 환경 : os, process, cluster
// - 파일, 경로, URL : fs, path, url, querystring, stream
// - 네트워크 : http, https, net, dgram, dns, ...

//모듈을 사용하려면 먼저 해당 모듈이 설치되어 있어야 하며 이를 불러와야 한다.
//사용방법: const 변수명 = require(모듈명);

//1. os 모듈
const os = require("os");
console.log(os.arch());	//x64	= process.arch
console.log(os.platform());	//linux = process.platform
console.log(os.type());	//Linux
console.log(os.uptime());
console.log(os.hostname());//goorm
console.log(os.homedir());// /root
console.log(os.cpus().length);

//2. path: 디렉토리나 파일의 경로를 처리하기 위한 모듈
//Windows의 경우 경로 구분자: \
//Unix(Linux, Mac)의 경우, 경로 구분자: /

const path = require("path");

//경로 정규화: 경로표현이 잘못되었을 때, 이를 정상적인 경로로 변환한다
console.log(path.normalize("/user/tmp/../local//\//bin/"))	//user/local/bin

//경로의 마지막 요소 추출하기
console.log(path.basename("/foo/bar/asdf/index.html"));	//index.html

//확장자를 제외한 이름만 추출하기
console.log(path.basename("/foo/bar/asdf/index.html", ".html"));	//index

//디렉토리만 추출하기
console.log(path.dirname("/foo/bar/asdf/index.html"));	//foo/bar/asdf

//확장자만 추출하기
console.log(path.extname("/foo/bar/asdf/index.html"));	//.html

//경로를 분석하여 추출하기
const data = path.parse("/home/user/hello.png");
console.log(data);	//root, dir, base, ext, name

//반대로 수행
const my_path = path.format({
	root: "/",
	dir: "/home/user",
	base: "file.txt",
	ext: "txt"
})

console.log(my_path);	// /home/user/file.txt

//path.sep : 경로 구분자로 윈도우의 경우 \, 유닉스 계열의 경우 /로 설정된다
console.log(__dirname + path.sep + "image.png");

//path.join(): 여러개의 경로를 연결하는 함수
console.log(path.join("/foo", "bar", "bar\qwerty", ".."));	//foo/bar (3,4번째 인자는 오류라서 join 안 됨)

//각 운영체제 별 기능을 제공한다
//Unix Family: path.posix.sep, path.posix.join
//Windows: path.win32.sep, path.win32.join
console.log(path.posix.sep, path.win32.sep);	// / \

 


<URL>

 

//6_url.js

const url = require("url");
const myurl = "http://www.hello.com:8080/p/a/t/h?name=kakaroo&age=20"

const parsed = url.parse(myurl);
console.log(parsed);

/*
Url {                                                                                prot
  slashes: true,
  auth: null,
  host: 'www.hello.com:8080',
  port: '8080',
  hostname: 'www.hello.com',
  hash: null,
  search: '?name=kakaroo&age=20',
  query: 'name=kakaroo&age=20',
  pathname: '/p/a/t/h',
  path: '/p/a/t/h?name=kakaroo&age=20',
  href: 'http://www.hello.com:8080/p/a/t/h?name=kakaroo&age=20'
}*/

//URL에서 query 부분만 파싱을 할 수 있도록 Node.js에서는 querystring 모듈을 제공한다.
console.log(parsed.query);//name=kakaroo&age=20
const qs = require("querystring");
const query = qs.parse(parsed.query);
console.log(query.name, query.age);	//kakaroo 20
//7_url.js

//기존의 Node.js 는 URL을 파싱하기 위해 url 모듈의 parse 함수를 제공했다.
//Node.js 7 이상부터는 URL을 좀 더 세분화하여 파싱할 수 있도록 URL 클래스를 제공한다.

const myurl = "http://www.hello.com:8080/p/a/t/h?name=kakaroo&age=20"
const url = new URL(myurl);
console.log(url);
/*
URL {
  href: 'http://www.hello.com:8080/p/a/t/h?name=kakaroo&age=20',
  origin: 'http://www.hello.com:8080',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'www.hello.com:8080',
  hostname: 'www.hello.com',
  port: '8080',
  pathname: '/p/a/t/h',
  search: '?name=kakaroo&age=20',
  searchParams: URLSearchParams { 'name' => 'kakaroo', 'age' => '20' },
  hash: ''
}
*/

const params = url.searchParams;
console.log(params);	//URLSearchParams { 'name' => 'kakaroo', 'age' => '20' }
console.log(params.get("name"), params.get("age"));	//kakaroo 20

//URLSearchParams 객체는 필요하면 단독으로 사용할 수 있다.
const p = new URLSearchParams("name=william&age=12&book=python");
console.log(p.get("name"), p.get("age")) //william 12

//get(key): 처음으로 일치하는 키만 가져온다
console.log(p.get("book"));
console.log(p.get("email")); //null //일치하는 키가 없는 경우 null이 반환

//has(key): 해당 키의 유무를 참/거짓 형태로 반환
console.log(p.has("age") ? p.get("age") : "no age");	//12

//getAll(key): 일치하는 모든 키를 가져온다
console.log(p.getAll("book")); //[ 'python' ] //value를 배열에 저장하여 반환

//append(key, value): 해당 키를 추가, 동일키가 존재하는 경우에도 해당 키를 추가함
p.append("book", "ruby");
console.log(p.getAll("book"));	//[ 'python', 'ruby' ]

//toString() : 조작한 객체를 문자열로 생성
console.log(p.toString());	//name=william&age=12&book=python&book=ruby

//set(key, value): 동일 키의 값들을 모두 지우고 새로 추가
p.set("book", "nodejs");
console.log(p.getAll("book"));	//[ 'nodejs' ]

//delete(key): 해당 키를 삭제
p.delete("book");
console.log(p.getAll("book"));	//[]

 

<FS>

//8_fs.js

//파일시스템 : 컴퓨터 상의 파일이나 자료를 쉽게 조작할 수 있도록 하는 관리프로그램
//Node.js는 컴퓨터 상의 파일이나 디렉토리를 쉽게 접근 및 조작을 수행할 수 있도록
//fs 모듈을 제공한다.

const fs = require("fs");

//1. 파일 읽기: hello.txt를 만들고 그 안에 'hello world'를 저장해보자
fs.readFile("./hello.txt", (err, data) => {
	if(err) throw err;
	
	//readFile 을 사용하여 파일을 읽어오면 데이터는 바이너리 형태로 전달된다.
	console.log(data);	//<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
	
	console.log(data.toString());	//hello world //문자 디코딩을 수행
})

//문자 인코딩 옵션 사용
fs.readFile("./hello.txt", "utf-8", (err, data) => {
	if(err) throw err;
	
	console.log(data);	//hello world 
})

//2. 파일 쓰기
fs.writeFile("./goodbye.txt", //해당파일이 없으면 생성 후, 쓰기를 수행
	 "goodbye, friend", //쓸 내용을 전달
	(err) => {
		if(err) throw err;
		console.log("create done!");	
});

//해당 파일이 이미 존재하는 경우, 덮어쓰기를 수행
fs.writeFile("./goodbye.txt", "hello,world", (err) => {
	if(err) throw err;
	console.log("overwrite done!");  //hello,worldiend
});

/*  비동기라서 순차적으로 출력되지는 않는다.
hello world
create done!
hello world
overwrite done!
*/

//3. 내용 추가하기
const arr = [1,2,3,4,5,6,7,8,9];
arr.forEach((item, idx, arr) => {
	//아래 함수는 비동기 함수이므로 파일 쓰기가 순차적으로 쓰여지지 않을 수 있다.
/*	fs.appendFile("./number.txt", `${item}\n`, (err) => {
		if(err) throw err;
	})
*/
	//동기 방식으로 처리하면 순차적으로 처리가 된다. 콜백 불필요
	fs.appendFileSync("./number.txt", `${item}\n`);
});

const userList = [
	{name: "kakaroo", age:20},
	{name: "william", age:12}
];
console.log(JSON.stringify(userList));
fs.writeFile("./user.json", JSON.stringify(userList), (err) => {
	if(err) throw err;
})	//[{"name":"kakaroo","age":20},{"name":"william","age":12}]

fs.readFile("./user.json", "utf-8", (err,data) => {
	if(err) throw err;
	const users = JSON.parse(data);
	console.log(users[0].name, users[0].age);	//kakaroo 20
	console.log(users[1].name, users[1].age);	//william 12
})

 

//9_fs.js

const fs = require("fs");	//콜백 버전
const fsp = require("fs").promises;	//프로미스 버전

//1. 콜백을 사용한 읽기
fs.readFile("./hello.txt", "utf-8", (err,data) => {
	if(err) throw err;
	console.log(data);
});

//2. 프로미스 버전을 사용한 읽기
fsp.readFile("./hello.txt", "utf-8")
	.then((data) => {console.log(data); })
	.catch((err) => {console.log(err); });

//3. 콜백을 사용한 쓰기
fs.writeFile("./goodbye.txt", "goodbye, friend", (err) => {
	if(err) throw err;
	console.log("done!");
})

//4. 프로미스 버전을 사용한 쓰기
fsp.writeFile("./goodbye.txt", "hello world")
.then(() => { console.log("Done-1"); })
.catch((err) => { console.log(err); });

 

<Buffer vs Stream>

//a_buffer.js

const fs = require("fs");

//파일 입출력 방식
//1. 버퍼 : 데이터를 모아서 처리하는 방식
//2. 스트림: 데이터를 흘려서 처리하는 방식

//버퍼의 사용  --> 아주 큰 데이터라면 문제가 된다.
const buf = Buffer.from("hello, world");
console.log(buf);
console.log(buf.length);	//버퍼의 크기로 단위는 바이트(byte)
console.log(buf.toString());//버퍼를 문자로 디코딩

const arr = [
	Buffer.from("apple "),
	Buffer.from("banana "),
	Buffer.from("cherry "),
	Buffer.from("durian")
];
const buf2 = Buffer.concat(arr);	//연결
console.log(buf2);
console.log(buf2.toString());//버퍼를 문자로 디코딩

 

//b_stream.js

const fs = require("fs");

//실습파일 생성 : 터미널에서 다음과 같이 입력
//# ls /bin > bin.txt

const rs = fs.createReadStream("./bin.txt", {highWaterMark:16});
//											^--- 한번에 읽을 크기(byte), 기본값은 64KB

//이벤트: 어떤 사건
//data: 읽을 데이터가 존재할 때 생성되는 이벤트
const dataArr = [];	//읽어온 데이터를 저장하기 위해 배열을 생성
rs.on("data", (chunk) => {
	dataArr.push(chunk);
	console.log("chunk:",chunk);
})
//end: 더 이상 읽을 데이터가 없을 때 생성되는 이벤트
rs.on("end", () => {
	console.log(Buffer.concat(dataArr).toString());
})
//error: 오류가 발생되었을 때 생성되는 이벤트
rs.on("error", (err) => {
	console.log("error:",err);
})

 

//1_stream.js

const fs = require("fs");

//쓰기
const ws = fs.createWriteStream("./write.txt");

//finish - 파일쓰기가 종료되었을 때 발생되는 이벤트
ws.on("finish", () => {
	console.log("done!");
});

//파일쓰기
ws.write("hello, world\n");
//					  ^--- 라인단위로 처리하려면 개행을 입력해야 함

ws.write("goodbye, friend!");
ws.end();	//쓰기가 완료되었다고 알려주는 메소드
반응형

'Node.js' 카테고리의 다른 글

Node.js - 수업 5일차  (0) 2022.08.26
Node.js - 수업 4일차  (0) 2022.08.25
Node.js - 수업 3일차  (0) 2022.08.24
Node.js - 수업 1일차  (0) 2022.08.22