Node.js

[생활코딩] Node.js 강의 12일차 (Web3-Express)

jadePark 2021. 7. 2. 18:26

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 로 압축돼 네트워크에 전송되는것을 확인할 수 있다. 

compression 적용 결과


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!')
});