[생활코딩] Node.js 강의 12일차 (Web3-Express)
Express 미들웨어의 사용 - body parser
Express의 중요한 기능은 1. 라우트 2.미들웨어 , 이 두가지가 있다.
미들웨어란? 서비스를 운영체계 상관없이 애플리케이션에 제공하는 소프트웨어이다.
Middleware is a type of computer software that provides services to software applications beyond those available from the operating system (Wikipedia)
쉽게 이야기해보자. 소프트웨어를 만들때 처음부터 끝까지 다 만드는 경우는 거의 없다. 미들웨어는 다른 사람이 만든 소프트웨어를 부품으로 쓸 수 있게 해준다. 정확한 정의는 아니지만, 다른 사람이 만든 소프트웨어를 미들웨어라고 치자. Third-party middleware는 express가 공식적으로 만들지 않고 다른 사람이 만든 미들웨어라는 뜻이다.
수업에서는 body-parser라는 미들웨어를 사용해봤다. (http://expressjs.com/en/resources/middleware/body-parser.html)
body : 웹 브라우저에서 요청한 정보의 본체. ( header: 그 본체를 설명하는 데이터)
body-parser : 그 본체인 데이터를 분석해서 필요한 형태로 가공해주는 프로그램
일단 body-parser가 설치가 됐는지 확인해주고 시작하겠다.
npm show body-parser version
설치가 안됐다면
npm install body-parser --save
body-parser main.js에 적용
app.use() 이 괄호 안은 body-parser가 만들어낸 미들웨어를 표현한다. main.js가 실행될때마다 괄호안의 미들웨어가 실행된다. body-parser는 사용자가 전송한 post 데이터를 내부적으로 분석해서 모든 데이터를 가지고 온 다음에 create_process, update_process, delete_process 각 라우터에 해당되는 callback 함수를 호출한다. 그렇게 약속됐다. 호출하면서 callback 함수의 첫번째 인자인 request 에 body property를 만들어준다. (원래 request에는 body property는 없었다.)
var express = require('express')
var app = express()
var bodyParser = require('body-parser');
/*생략*/
app.post('/create_process',function(request,response){
var post = request.body;
var title = post.title;
var description = post.description;
fs.writeFile(`data/${title}`,description,'utf8',
function(err){
response.redirect(`/?id=${title}`);
})
/* 이전 코드
var body = '';
request.on('data',function(data){
body = body + data;
});
request.on('end',function(){
var post = qs.parse(body);
var title = post.title;
var description = post.description;
fs.writeFile(`data/${title}`,description,'utf8',
function(err){
response.redirect(`/?id=${title}`);
})
});
*/
});
/*생략*/
Express 미들웨어의 사용 - compression
compression 설치가 됐는지 확인해주고 시작하겠다. (http://expressjs.com/en/resources/middleware/compression.html)
npm show compression version
설치가 안됐다면
npm install compression --save
compression main.js 적용
compression을 호출하면 미들웨어를 리턴하도록 약속되었고, 이 미들웨어가 app.use로 장착된다. 그러면 우리 애플리케이션은 요청이 들어올때마다 body parser 미들웨어가 호출되고 compression 미들웨어가 실행되길 약속되었다.
var express = require('express')
var app = express()
var fs = require('fs');
var path = require('path');
var sanitizeHtml =require('sanitize-html');
var bodyParser = require('body-parser');
var compression = require('compression');
var template = require('./lib/template.js');
var qs = require('querystring');
app.use(bodyParser.urlencoded({extended:false}));
app.use(compression());
/*생략*/
(강제로 리로드 - 윈도우: ctrl +shift +R / 맥 : cmd+shift+R)
compression이 적용되고 원래 33.3 kB가 4.9kB 로 압축돼 네트워크에 전송되는것을 확인할 수 있다.
Express 미들웨어 만들기
폴더에서 파일을 읽어오는 아래 코드가 곳곳에서 사용돼 미들웨어로 만들어주는 작업을 했다.
fs.readdir('./data',function(error,filelist){/*생략*/});
먼저 app.use로 요청이 들어올때마다 수행하는 코드를 작성해봤다.
app.use(function(request,response,next){
fs.readdir('./data',function(error,filelist){
request.list =filelist;
next();
});
});
하지만 post 방식인, create_process, update_process, delete_process에는 위 코드가 필요가 없다. 그럼 안읽어주는게 효율적이기에 get방식인 라우터만 적용되게 코드를 바꿔줬다.
app.get('*',function(request,response,next){
fs.readdir('./data',function(error,filelist){
request.list =filelist;
next();
});
});
위 코드와 아래코드를 비교해보면, app.get의 두번째 인자는 미들웨어라는 것을 알 수 있다. Express에서는 모든게 middleware라고 할 수 있다.
app.get('/', function(request, response){
var title = 'Welcome';
var description = 'Hello Node.js'
var list = template.list(request.list);
var html = template.html(title,list,`<h2>${title}</h2>${description}`,
`<a href ="/create">create</a>`);
response.send(html);
});
애플리케이션이 구동될때, 순서대로 등록된 작은 프로그램들이 실행되고 각각의 프로그램들이 서로서로 연결해주는 작은 소프트웨어라는 점에서 'middleware'라는 표현을 쓰는 듯하다.
Express 미들웨어의 실행순서
우리가 만든 미들웨어는 Application-level middleware 강의에서는 이것만 다룸, 더 자세한 내용은 여기서 확인하자.
app.use(함수)를 작성하면 미들웨어가 등록된다. 미들웨어의 핵심은 request와 response 객체를 받아서 변형할 수 있다. next라는 함수를 호출해서 다음에 실행되어야할 미들웨어를 실행할지 실행하지 않을지 , 그 미들웨어의 이전 미들웨어가 결정하도록 한다는 것이 핵심이다.
var app = express()
app.use(function(req,res,next){
console.log('Time:'.Date.now())
next()
})
미들웨어가 여러개 등록될수있다. 1번함수가 실행되고 next를 호출하면, 2번함수를 호출하는 것과 다름 없음. 순서대로 실행됨.
app.use('/user/:id', /*1번함수*/ function(req, res, next) {
console.log('Request URL:', req.originalUrl);
next();
},/*2번함수*/ function (req, res, next) {
console.log('Request Type:', req.method);
next();
});
조건문을 통해 처리할 수도 있다. user ID가 0이면 다음 라우트 3번이 실행되고, 아니라면 같은 라우트 안의 2번이 실행된다.
app.get('/user/:id', /*1번*/function (req, res, next) {
// if the user ID is 0, skip to the next route
if (req.params.id == 0) next('route');
// otherwise pass the control to the next middleware function in this stack
else next(); //
}, /*2번*/function (req, res, next) {
// render a regular page
res.render('regular');
});
// handler for the /user/:id path, which renders a special page
app.get('/user/:id',/*3번*/ function (req, res, next) {
res.render('special');
});
강의 코드
var express = require('express')
var app = express()
var fs = require('fs');
var path = require('path');
var sanitizeHtml =require('sanitize-html');
var bodyParser = require('body-parser');
var compression = require('compression');
var template = require('./lib/template.js');
var qs = require('querystring');
app.use(bodyParser.urlencoded({extended:false}));
app.use(compression());
app.get('*',function(request,response,next){
fs.readdir('./data',function(error,filelist){
request.list =filelist;
next();
});
});
app.get('/', function(request, response){
var title = 'Welcome';
var description = 'Hello Node.js'
var list = template.list(request.list);
var html = template.html(title,list,`<h2>${title}</h2>${description}`,
`<a href ="/create">create</a>`);
response.send(html);
});
app.get('/page/:pageId', function(request, response){
var filteredID = path.parse(request.params.pageId).base;
fs.readFile(`data/${filteredID}`,'utf8',function(err,description){
var title = request.params.pageId;
var sanitizedTitle = sanitizeHtml(title);
var sanitizedDescription = sanitizeHtml(description,{
allowedTags:['h1']
});
var list = template.list(request.list);
var html= template.html(title,list,`<h2>${sanitizedTitle}</h2>${sanitizedDescription}`,
`<a href ="/create">create</a>
<a href="/update/${sanitizedTitle}">update</a>
<form action="/delete_process" method="post" >
<input type="hidden" name ="id" value="${sanitizedTitle}">
<input type="submit" value="delete">
</form>`);
response.send(html);
});
});
app.get('/create',function(request,response){
var title = 'Web - create';
var list = template.list(request.list);
var html = template.html(title,list,`
<form action="/create_process" method="post">
<p><input type="text" name="title" placeholder="text"></p>
<p>
<textarea name="description" placeholder="description"></textarea>
</p>
<p>
<input type="submit">
</p>
</form>
`,'');
response.send(html);
});
app.post('/create_process',function(request,response){
var post = request.body;
var title = post.title;
var description = post.description;
fs.writeFile(`data/${title}`,description,'utf8',
function(err){
response.redirect(`/?id=${title}`);
})
});
app.get('/update/:pageId', function(request, response){
var filteredId = path.parse(request.params.pageId).base;
fs.readFile(`data/${filteredId}`, 'utf8', function(err, description){
var title = request.params.pageId;
var list = template.list(request.list);
var html = template.html(title, list,
`
<form action="/update_process" method="post">
<input type="hidden" name="id" value="${title}">
<p><input type="text" name="title" placeholder="title" value="${title}"></p>
<p>
<textarea name="description" placeholder="description">${description}</textarea>
</p>
<p>
<input type="submit">
</p>
</form>
`,
`<a href="/create">create</a> <a href="/update/${title}">update</a>`
);
response.send(html);
});
});
app.post('/update_process', function(request, response){
var post = request.body;
var id = post.id;
var title = post.title;
var description = post.description;
fs.rename(`data/${id}`, `data/${title}`, function(error){
fs.writeFile(`data/${title}`, description, 'utf8', function(err){
response.writeHead(302, {Location: `/page/${title}`});
response.end();
});
});
});
app.post('/delete_process',function(request,response){
var post = request.body;
var id = post.id;
var filteredID = path.parse(id).base;
fs.unlink(`data/${filteredID}`,function(error){
response.redirect('/');
});
});
app.listen(3000, function(){
console.log('Example app listening on port 3000!')
});