이벤트(ex 클릭 엔터 등...)가 발생할 때 사전에 짜여진 작업을 수행하는 것. 즉 특정 이벤트 발생 시 거기에 설정해둔 콜백함수를 호출.
호출된 함수들은 순서대로 호출 스택에 들어가고 함수 실행이 완료되면 호출스택에서 마지막에 들어간 호출부터 지워진다.
즉 후입선출(LIFO : Last In First Out)
이전 작업이 완료되기 전에 다른 작업을 수행하는 것.
(즉 블로킹은 이전 작업이 모두 완료된 후에 다른 작업 수행 가능.)
노드는 I/O 작업을 백그라운드에 넘겨 동시에 처리
I/O란?
"주로 libuv가 지원하는 시스템 디스크나 네트워크와 상호작용하는 것"
즉 파일을 읽거나 파일쓰기 폴더만들기 등 파일시스템접근이나 네트워크를 통한 요청
등이 이에 해당됨.
* libuv : 이벤트 루프를 기반으로 비동기 I/O를 지원하는 다중 플랫폼 C 라이브러리
논블로킹 방식으로 코드를 짜는 것은 작업 실행 성능을 높여준다.
논블로킹 기법
setTimeout(콜백,0)
function longRunningTask() { // 오래 걸리는 작업 console.log('작업 완료'); }; console.log('시작'); setTimeout(longRunningTask,0); console.log('다음작업'); // 위의 코드 실행시 // 시작 -> 다음작업 -> 작업완료
📌 논블로킹 예시
const fs = requir('fs');
fs.readFile('/file.md',(err,data) => {
if(err) throw err;
console.log(data);I/O
});
fs.unlinkSync('/file.md');
// 위 코드의 문제점은 I/O가 논블로킹 방식으로 이루어지기 때문에 앞에서 fs 파일이
// 다 읽혀지기도 전에 unlinkSync()가 실행되어 파일이 제거될 위험이 있다.
// 위의 코드는 아래와 같이 수정되어야 함.
const fs = require('fs');
fs.readFile('/file.md',(readFileErr,data) => {
if(readFileErr) throw readFileErr;
console.log(data);
fs.unlink('/file.md',(funlinkErr) => {
if(unlinkErr) throw unlinkErr;
})
})
🌞 이해를 돕는 아주 좋은 예시
점원이 한 손님의 주문을 받고 주방에 주문내역을 넘긴 뒤 다음 손님의 주문을 받습니다.
요리가 끝나기 까지 기다리지 않고, 주문내역만 주방에 계속 전달하고, 주방에서 요리가
완료되면 완료된 순서대로 손님에게 서빙합니다. 요리의 특성(블로킹인지 논블로킹인지)에
따라 완료되는 순서가 달라질 수 있어, 주문의 들어온 순서와 서빙하는 순서가 일치하지
않을 수 있습니다.
// 요리를 하는 시간이 오래 걸린다면(CPU가 많이 소모되는 작업) 주문이 많이 들어오면
// 버거울 수 있다.
즉 싱글스레드 == 점원 한 명
예외 )
스레드풀(Thread Pool) : 특정 동작 수행시 멀티 스레드로 실행
워커 스레드(Thead Pool) : 노드 12 버전이후 안정화된 기능, 멀티스레드 가능.
// 터미널에 node 입력후
process 객체는 현재 실행되는 노드 프로세스에 대한 정보 제공
process.version
: 노드 버전 제공
process.arch
: 프로세서 아키텍처 정보 제공
process.platform
: 운영체제 플랫폼 정보 제공
process.pid
: pid 제공
process.exePath
: 노드의 경로 정보 제공
process.cpuUsage
: 현재 cpu 사용량 정보 제공
process.env
: 환경변수에 대한 정보 제공
process.nextTick
: 이벤트 루프가 다른 콜백 함수들보다 nextTick()의 콜백 함수를 우선처리하도록 함.
// setImmediate setTimeout보다 먼저 실행됨.
// resolve된 Promise도 nextTick처럼 다른 콜백들보다 우선시됨
ex)
setImmediate(() => {
console.log('immediate');
});
process.env.nextTick(() => {
console.log('nextTick');
});
setTimeout(() => {
console.log('timeout');
},0);
Promise.resolve().then(() => console.log('promise'));
//출력순서
// nextTick -> promise -> timeout -> immediate
process.exit
: 실행중인 노드 프로세스 종료
* 이벤트루프
이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백 함수의 실행순서를 결정하는
역할.
❕ 동기와 블로킹
❕ 비동기와 논블로킹
설치된 패키지의 버전을 관리해주는 파일
> npm init
// 터미널에 입력 package.json파일 생성
> npm install [패키지명]
// 해당 패키지 설치해줌
> npm install --save-dev
// 실제 배포시에는 사용x 개발시에만 사용하는 패키지
// package.json
// --save-dev 옵션과 설치된 모듈은 devDependencies에서 관리.
"devDependencies": {
"nodemon": "^2.0.15"
}
> npm install -global rimraf
// 전역설치한 것으로, 이는 package.json에 기록x
> npm install -global rimraf
// 리눅스나 맥의 rm -rf 명령어를 윈도우에서도 사용가능하게 해주는 패키지
npx
전역설치대신 사용
1. npm install --save-dev rimraf
2. npx rimraf [삭제할것]
// 위와 같이 사용시에 npx를 붙여 사용.
> npm uninstall [패키지명]
// 사용하지 않는 패키지 삭제
JavaScript로 작성되고 Node.js 런타임 환경에서 구동되는 웹프레임워크
미들웨어 실행
app.use(미들웨어) // 모든 요청에서 해당 미들웨어 사용
app.use('/login',미들웨어) // /login을 통한 요청시에만 해당 미들웨어 사용
app.post('logout',미들웨어) // post메서드이면서 /login을 통한 요청시에만 해당 미들웨어 사용.
// 예시
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser')
// body-parser는 본문의 데이터를 해석하여 req.body객체로 전달해주는 기능을 하는 미들웨어.
// 이미지, 동영상, 파일등의 멀티파트 이미지는 해석x 멀티파트 데이터는 multer모듈 사용.
const cookieParser = require('cookie-parser');
// 요청을 통해 전달된 쿠기를 해석애 req.cookies 객체로 만듦.
const session = require('express-session');
require('dotenv').config();
const path = require('path');
const app = express();
app.set('port',proscess.env.PORT||3000);
// app.set(키,값)을 사용해서 데이터 저장. 데이터 가져올 때는 app.get(키)로 가져옴.
app.use(morgan('dev')); // dev combined common short tiny 등
// dev 기준 [HTTP메서드] [주소] [HTTP상태코드] [응답속도] - [응답바이트]
app.use('/',express.static(path.json(__dirname,'public')));
// static 미들웨어는 정적인 파일들을 제공하는 라우터 역할.
// 아래의 두 줄이 body-parser사용을 위한 세팅 코드
app.use(express.json());
app.use(express.urlencoded({extended:false}));
// app.use(bodyParser.raw()); // 요청의 본문이 버퍼 데이터일때 추가 세팅
// app.use(bodyParser.text()); // 요청의 본문이 텍스트 데이터일때 추가 세팅
app.use(cookieParser(process.env.COOKIE_SECRET));
//app.use(cookieParser(비밀키))
/*
res.cookie('name','algml', {
expires:new Date(Date.now() + 900000),
httpOnly:true,
secure:true
// signed:true // 쿠키를 만든 것 검증해줌.
});
res.clearCookie('name','algml',{httpOnly:true,secure:true})
*/
//240page
app.use(session({
resave:false,
saveUninitialized:false,
secret:process.env.COOKIE_SECRET,
cookie:{
httpOnly:true,
secure:false,
},
name:'session-cookie',
}))
app.use((req,res,next) => {
console.log('모든 요청에 실행');
next();
})