세네카와 익스프레스 연동

세네카는 웹 프레임워크가 아니기에 Express와 같은 웹 프레임워크와 연동을 해야 한다.

필요한 모듈을 설치해보자

npm install --save express
npm install --save seneca-web
npm install --save seneca-web-adapter-express

익스프레스와 세네카를 연동시키기 위해서는 seneca-web 모듈뿐만 아니라 adapter가 필요하다. seneca-web-adapter-express 모듈 역시 설치하고 세네카에 적용시켜주도록 하자.

var SenecaWeb = require('seneca-web');
var Express = require('express');
var Router = Express.Router;
var context = new Router();
 
var senecaWebConfig = {
      context: context,
      adapter: require('seneca-web-adapter-express'),
      options: { parseBody: false } // so we can use body-parser
};
 
var app = Express()
      .use( require('body-parser').json() )
      .use( context )
      .listen({port:3000});
 
var seneca = require('seneca')()
      .use(SenecaWeb, senecaWebConfig )
      // api 모듈
      .use('api')
      .client( { type:'tcp', pin:'role:math' } );

세네카와 익스프레스의 연동을 위한 부분은 아래와 같다.

.use(SenecaWeb, senecaWebConfig)
//This code means
//seneca.act('role:web', {routes:routes})

api 테스트

테스트를 위해 간단한 api를 작성해보도록 하자.

//api.js
 
module.exports = function api(options) {
  var valid_ops = { sum:'sum', product:'product' };
 
  this.add('role:math, cmd:product', function(msg, respond) {
    var product = msg.left * msg.right;
    respond(null, { answer: product});
  });
 
  this.add('role:math, cmd:sum', function(msg, respond) {
    var sum = parseInt(msg.left) + parseInt(msg.right);
    respond(null, {answer: sum});
  });
 
  this.add('role:api, path:calculate', function (msg, respond) {
    var operation = msg.args.params.operation;
    console.log(operation);
 
    var left = msg.args.query.left;
    var right = msg.args.query.right;
 
    this.act('role:math', {
      cmd:   valid_ops[operation],
      left:  left,
      right: right,
    }, respond);
  });
 
  this.add('init:api', function (msg, respond) {
    this.act('role:web',{routes:{
      prefix: '/api',
      pin:    'role:api,path:*',
      map: {
        //GET, POST 모두 호출 가능하게 함.
        calculate: { GET:true, POST: true, suffix:'/:operation' }
      }
    }}, respond);
  });
};

위의 role:web 패턴은 routes의 속성을 정의하고 있습니다.

  • prefix : URL

  • pin : map으로 전달될 패턴 집합

  • map : pin에서 * 로 매칭된 집합 리스트이며 URL의 엔드포인터로서의 값

init은 그냥 패턴일 줄 알았는데 세네카의 속성값이었다. url호출시 init:api 부분을 호출한다.

this.add('init:api')

POST로 요청해보자

GET과 같은 원리로 POST를 추가해보았는데 라우터 설정은 잘 되어있는 거 같은데 계속 body를 읽을 수 없다고 한다. 그래서 express처럼 bodyParser추가하는 방식으로 해보았더니 잘 되었다.

//app.js
 
...
var bodyParser = require('body-parser');
...
 
var app = Express();
 
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
 
app.use( context );
app.listen(3000);
 
...

this.add(init:api)의 map 부분에 user라는 path를 추가하였다.

//api.js
 
...
 
this.add('init:api', function (msg, respond) {
  this.act('role:web',{routes:{
    prefix: '/api',
    pin:    'role:api,path:*',
    map: {
      calculate: { GET:true, suffix:'/:operation' },
      user: { POST:true }
    }
  }}, respond);
});

seneca에 해당 패턴을 추가한다.

//api.js
 
this.add('display:userPw', function(msg, respond) {
  respond(null, {userPw: msg.userPw});
});
 
this.add('role:api, path:user', function (msg, respond) {
  console.log(msg);
  this.act('display:userPw', {
    userPw: msg.args.body.userPw
  }, respond);
});

테스트를 위해 포스트맨으로 x-www-form-urlencoded 방식으로 호출하였더니 다음과 같이 출력이 됨을 확인할 수 있다.

{
  "userPw": 1234
}

마치며

post로 데이터 전송을 하는데 계속 데이터 출력이 안되서 엄청 삽질을 하였다. 결국 익스프레스 사용할 때처럼 bodyParser를 적용하였다. 그래도 seneca 설정부분에서 options: { parseBody: false } 은 해줘야하는 것 같다. 이 부분을 제거해보니 전송이 안됨을 확인하였다.

'Framework > Nodejs' 카테고리의 다른 글

express-session  (0) 2018.01.03
pm2에 대해 알아보자  (0) 2017.10.06
[마이크로서비스] Seneca  (0) 2017.10.02
비동기와 CPU Bound  (0) 2017.09.04
node js, mongodb 연동 with mongoose  (0) 2017.08.16

Seneca란?

세네카란 마이크로서비스를 구축하기 위해 작성된 프레임워크이다. 코드에서 전송을 추상화하는 정교한 패턴 매칭 인터페이스를 마이크로서비스에 연결된다. 따라서 세네카를 이용하면 확장성이 높은 아키텍쳐를 쉽게 구축할 수 있다.

install

npm install -g seneca

패턴매칭

seneca.add 를 통해 패턴을 세네카에 패턴이 집합으로 새로운 함수를 추가한다.seneca.act 를 통해 전송된 패턴과 일치하는 서비스에 의해 명령을 실행될 세네카로 전송한다.

var seneca = require('seneca')();
 
seneca.add({role: 'math', cmd: 'sum'}, function(msg, respond) {
  var sum = Number(msg.left) + Number(msg.right);
  respond(null, {answer: sum});
});
 
seneca.add({role: 'math', cmd: 'product'}, function(msg, respond) {
  var product = msg.left * msg.right;
  respond(null, {answer: product});
});
 
// {answer: 3}
seneca.act({role: 'math', cmd: 'sum', left: 1, right: 2}, console.log);
 
// {answer: 12}
seneca.act({role: 'math', cmd: 'product', left: 3, right: 4}, console.log);

패턴 재사용

등록한 패턴을 다음과 같이 재사용할 수 있다.

seneca.add({role: 'math', cmd: 'sum'}, function(msg, respond) {
  var sum = msg.left + msg.right;
  respond(null, {answer: sum});
});
 
seneca.add({role: 'math'}, function(msg, respond) {
  this.act({
    role: 'math',
    cmd: 'sum',
    left: msg.left,
    right: msg.right
  }, respond);
});
 
//{answer: 7}
seneca.act({role: 'math', left: 3, right: 4}, console.log);

매턴 매칭은 어떻게 동작하는가?

매칭된 가장 긴 체인이 적합한 패턴이 된다.

{x:1}과 {x:1, y:2}가 패턴으로 등록되어있다고 가정해보자.

  • {x:1} 요청 -> {x:1}가 가장 적합

  • {x:1, y:2} 요청 -> {x:1, y:2}와 적합

  • {x:1, y:2, z:3} 요청 -> {x:1, y:2}와 적합

  • {y:2} -> 매칭된 패턴 없음.(앞에서부터 매칭 필요)

플러그인

아래와 같이 세네카 외부 함수를 만들고 use를 통해 세네카에 적용시켜 사용할 수 있다.

//math.js
 
function math(name) {
  var name = name;
 
  this.add('role:math, cmd:sum', function(msg, respond) {
    respond(null, {name: Number(msg.left) + Number(msg.right)});
  });
 
  this.add('role:math, cmd:product', function(msg, respond) {
    respond(null, {name: msg.left * msg.right});
  });
}
 
//{bbo: 3}
require('seneca')()
  .use(math, 'bbo')
  .act('role:math, cmd:sum, left:1, right:2', console.log);

마이크로서비스 작성 예시

module.exports = function math(options) {
 
  this.add('role:math,cmd:sum', function sum(msg, respond) {
    respond(null, { answer: Number(msg.left) + Number(msg.right) });
  });
 
  this.add('role:math,cmd:product', function product(msg, respond) {
    respond(null, { answer: msg.left * msg.right });
  });
 
  this.wrap('role:math', function (msg, respond) {
    msg.left  = Number(msg.left).valueOf();
    msg.right = Number(msg.right).valueOf();
    this.prior(msg, respond);
  });
};

위의 코드에서 주의깊게 봐야할 2개의 함수가 있다.

  • seneca.wrap 함수는 패턴과 매칭된 집합들을 같은 action으로 묶는다.

  • seneca.prior 함수는 이전의 정의된 action 을 호출한다. wrap을 통해 확장된 action을 호출후 내부에서 패턴과 일치하는 이전 action을 호출!!

require('seneca')()
  .use('math')
  .act('role:math, cmd:sum, left:3, right:4', console.log);
 
require('seneca')()
  .use('math')
  .act('role:math, cmd:product, left:3, right:4', console.log);

서버 실행 - listen()

require('seneca')()
  .use(math)
  .listen({type: 'tcp', pin: 'role:math'});

curl을 이용하여 패턴과 함께 url을 실행시킨다.(url/act)

$ curl -d '{"role":"math", "cmd":"sum", "left":1, "right":2}' http://localhost:3000/act

클라이언트 접속 - client()

require('seneca')()
// 로컬에 패턴 추가
.add('say:hello', function(msg, respond) {
  respond(null, {text: "Hi!"});
})
// 서버의 listen 매칭과 일치해야 한다.
// 위의 listen() 시 pin: 'role:math'로 하였으므로
// client() 에도 pin: 'role:math'가 되어야 한다.
.client({type: 'tcp', pin: 'role:math'})
// math 모듈의 패턴과 매칭
.act('role:math, cmd:sum, left:1, right:2',console.log)
// 로컬의 패턴과 매칭
.act('say:hello', console.log)
.act('role:math, cmd:product, left:3, right:3', console.log);

출력은 다음과 같을 것이다.

{text: 'hello'}
{answer: 3}
{answer: 9}

역시 비동기로 출력이 됨을 알 수가 있다.

마치며..

마이크로 서비스를 작성하기 앞서 세네카라는 프레임워크에 대해서 조사를 해보았다. 세네카를 사용하면 모듈단위로 확장할 때 패턴 매칭을 이용하기 때문에 확장성과 테스트 단위가 쉬울 것 같다. 다음 시간에는 express와 세네카를 적용해보아야겠다.

'Framework > Nodejs' 카테고리의 다른 글

pm2에 대해 알아보자  (0) 2017.10.06
[마이크로서비스] 세네카와 익스프레스 연동  (0) 2017.10.02
비동기와 CPU Bound  (0) 2017.09.04
node js, mongodb 연동 with mongoose  (0) 2017.08.16
이메일 발송  (0) 2017.07.13

비동기에 대해 생각해보자

nodejs가 흔히 비동기로 동작을 한다고 알고 있습니다. 비동기와 동기를 비교해 보면 동기는 함수가 호출되어 반환될 때까지 대기시간이 발생하지만 비동기는 동기적 코드의 반환값이 비동기적 코드에서는 콜백의 인자로 넘어오게 됩니다. 이는 반환값이 올때까지 기다리는 불필요한 대기시간을 해결해줍니다.

CPU Bound vs IO Bound

비동기로 작동하게 되면 위와 같은 좋은 점도 있지만 흐름을 제어하기가 어렵다는 점이 있습니다. 그래서 Promise 과 같은 모듈을 사용하지만 사용하기 전에 CPU bound와 IO Bound에 대해서 알고 사용해야 한다는 것을 알게 되었습니다.

아래와 같은 코드는 어떻게 동작할까요?

hashMap.forEach(function(value, key) {
  console.log(key + ' : ' + value);
})

hashMap에 존재하는 key, value값을 차례대로 출력하게 됩니다. 분명 forEach안에서 callback으로 작동하는데 말이죠. hashMap이라고 다른게 아니라 이는 접근하려고 하는 메모리가 inMemory이기 때문입니다.(인 것 같습니다..)

우선 이를 이해하기 위해서는 CPU bound와 I/O Bound란 무엇인지 알아야합니다.

  • CPU Bound는 CPU 자원을 사용하는 Task이며 작업 속도가 빠르며 javascript의 v8엔진에 의해 처리가 됩니다.

  • I/O Bound는 Input/Output이 Disk, network, Database와 관련된 Task이며 Event Loop가 돌면서 Event Queue에 쌓인 Message를 처리합니다.

CPU 자원을 사용하는 CPU Bound는 v8엔진에 의해 처리되는데 이는 동기적으로 처리가 됩니다. 그리고 우리가 아는 nodejs가 비동기적으로 처리한다는 것은 I/O Bound을 말합니다. 이를 잘 이해하여 동기적으로 돌아가는 것과 비동기적으로 돌아가는 코드를 구분할 수 있어야 할 것 같습니다...

Node js는 CPU Bound 작업을 처리하는데 적합한가

먼저 nodejs는 싱글 스레드 기반(Event Loop)으로 돌아갑니다. (내부적으로는 Non-Blocking을 지원하지 않는 I/O를 처리하기 위해 Multi Thread Pool을 이용합니다.)

다른언어와 비교해 보면 다른 언어는 두 개의 요청이 오면 번갈아 가면서 요청을 처리하지만 node js는 하나의 작업을 처리하고 다음 요청을 처리합니다. 이는 node js가 요청간의 cpu 낭비를 최소화하기 때문에 빠르다고 할 수 있지만 하나의 작업의 CPU burst time이 길어지면 그만큼 다른 요청의 처리가 늦어지고 성능의 저하로 느껴질 수 있습니다.




참고 사이트


'Framework > Nodejs' 카테고리의 다른 글

[마이크로서비스] 세네카와 익스프레스 연동  (0) 2017.10.02
[마이크로서비스] Seneca  (0) 2017.10.02
node js, mongodb 연동 with mongoose  (0) 2017.08.16
이메일 발송  (0) 2017.07.13
MVC model  (0) 2017.07.07

Mongodb 연동

node js 와 mongodb 를 mvc 패턴에 맞게 설계를 하였습니다. 같은 mvc 패턴이어도 model 부분에다가 mongoose.model 을 exports 하는 경우가 일반적이던데 어떤 방식이 맞다 틀리다는 아직 모르겠습니다.

app.js

app.js에서는 각종 모듈을 불러옵니다. catalog는 라우팅을 위한 모듈로 정의하였습니다.

var express = require('express');
var app = express();
var catalog = require('./api/routes/catalog');
var bodyParser = require('body-parser');
 
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
 
// mongoose require
var mongoose = require('mongoose');
 
//testDB setting
mongoose.connect('mongodb://localhost:27017/testDB');
 
// 3. use testDB
var db = mongoose.connection;
 
// 4. connect error
db.on('error', function(){
    console.log('Connection Failed!');
});
 
// 5. connect success
db.once('open', function() {
    console.log('Connected!');
});
 
app.use('/', catalog);
 
app.listen(3000, function() {
  console.log('server port 3000');
});

catalog.js

라우팅 모듈로서 정의하였습니다.

var express = require('express');
var router = express.Router();
 
var userController = require('../controllers/userController');
 
router.get('/', function (req, res) {
  console.log('hello world');
  res.end('hello world');
});
 
//user router
router.post('/users', userController.createUser);
router.get('/users/:uid', userController.getUser);
router.put('/users/:uid', userController.updateUser);
router.delete('/users/:uid', userController.deleteUser);
 
module.exports = router;

userModel.js

mongoose.Schema 를 통해 Schema를 정의합니다. mongoose.model을 통해 mongoDB의 collection을 생성할 수 있습니다. 아래 예제를 통해 mongoDB 콜솔에서 testDB에 show collections 을 입력하면 users라는 collection이 생성이 되었음을 확인할 수 있습니다.

!model을 user라고 정의하니 users라는 복수형의 컬렉션 네임이 지정되었습니다. 이 이유는 잘 모르겠네요

var mongoose = require('mongoose');
 
var Schema = mongoose.Schema;
 
var userSchema = new Schema({
  "userId": String,
  "userPw": String,
  "modifyDate" : Date
});
 
module.exports = mongoose.model('user', userSchema);

userController.js

해당 모듈을 불러옵니다.

var mongoose = require('mongoose');
var User = require('../models/userModel');

데이터 저장을 위해 save()함수를 이용합니다.

exports.createUser = function(req, res) {
  console.log(req.body);
  var newUser = new User(req.body);
  newUser.modifyDate = new Date();
 
  newUser.save(function(error, data) {
    if(error) {
      console.log(error);
    } else {
      console.log('Saved');
    }
  });
  res.end('createUser');
};

데이터를 얻기 위해 findOne() 함수를 이용합니다. findById 는 _id의 데이터를 기준으로 하여 얻을 수 있습니다. 데이터 전체를 얻기 위해서는 find() 함수를 이용합니다.

exports.getUser = function(req, res) {
  User.findOne({_id : req.params.uid}, function(error, user) {
    console.log('--- Read one user---');
    if(error) {
      console.log(error);
    } else {
      console.log(user);
    }
  });
  res.end('getUser');
};

특정 데이터 수정 부분입니다.

exports.updateUser = function(req, res) {
  User.findById({_id : req.params.uid}, function(error, user) {
    console.log('--- Update user ---');
    if(error) {
      console.log(error);
    } else {
      user.userPw = req.body.userPw;
      user.modifyDate = new Date();
 
      user.save(function(error, modify_user) {
        if(error) {
          console.log(error);
        } else {
          console.log(modify_user);
        }
      });
    }
  });
  res.end('updateUser');
};

특정 데이터 삭제 부분입니다.

exports.deleteUser = function(req, res) {
  User.remove({_id : req.params.uid}, function(error, output) {
    console.log('--- Delete ---');
    if(error) {
      console.log(error);
    } else {
      console.log(output);
    }
  });
 
  res.end('deleteUser');
};


'Framework > Nodejs' 카테고리의 다른 글

[마이크로서비스] Seneca  (0) 2017.10.02
비동기와 CPU Bound  (0) 2017.09.04
이메일 발송  (0) 2017.07.13
MVC model  (0) 2017.07.07
mocha  (0) 2017.07.05

nodemailer

nodemailer는 유저의 계정 정보를 받아 들여야 하는데 계정 정보의 노출이 우려가 된다.

이 부분에 대해 해결할 방법을 추후 공부해보도록 해야겠다.

install

npm install nodemailer --save

app.js

var nodemailer = require('nodemailer');
var config = require('./config/config');
var smtpTransport = nodemailer.createTransport({
    service: 'Gmail',
    auth: {
        user: config.user,
        pass: config.pass
    }
});
 
var mailOptions = {
    from: 'KAPTest <konanbohyuk@gmail.com>',
    to: to,
    subject: 'Nodemailer 테스트',
    text: '평문 보내기 테스트 ',
    html:'<h1>HTML 보내기 테스트</h1><p><img src="http://www.nodemailer.com/img/logo.png"/></p>'
};
 
smtpTransport.sendMail(mailOptions, function(error, response){
    if (error){
        console.log(error);
    } else {
        console.log("Message sent : " + response.message);
    }
    smtpTransport.close();
});

config.js

module.exports = {
  user: "####@gmail.com",
  pass: "###"
};


'Framework > Nodejs' 카테고리의 다른 글

비동기와 CPU Bound  (0) 2017.09.04
node js, mongodb 연동 with mongoose  (0) 2017.08.16
MVC model  (0) 2017.07.07
mocha  (0) 2017.07.05
미들웨어(1):cookieParser, bodyParser, limit, query  (0) 2017.04.05

MVC 모델

api 서버를 구축하기 위해 mvc 모델을 구축해보았다.
db는 mongodb를 이용하였고 연동시켜 보았다.

routes에서 요청을 받고 controllers로 요청을 하는 그림을 보고 https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/routes 링크를 참조하며 공부한 결과 아래와 같은 mvc 패턴을 구현하였다.

app.js

var express = require('express');
var http = require('http');
var bodyParser = require('body-parser');
 
var app = express();
 
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
 
app.listen(8888);
 
console.log("Server is Running...");
 
var catalog = require('./api/routes/catalog');
 
app.use('/', catalog);
 

userController.js

(function() {
  'use strict';
})();
 
var User = require('../models/userModel.js');
 
exports.get_dummy = function (req, res) {
  res.end('get_dummy');
};
 
exports.put_dummy = function (req, res) {
  res.end('put_dummy');
};
 
exports.post_dummy = function (req, res) {
  var userIns = User();
  userIns.name = 'jung';
  res.end(userIns.name);
};
 
exports.delete_dummy = function (req, res) {
  res.end('delete_dummy');
};

catalog.js(route)

catalog.js 에서 각 controllers를 추가하여 라우팅을 해준다.

(function() {
  'use strict';
})();
 
var express = require('express');
var router = express.Router();
 
var userController = require('../controllers/userController');
 
router.get('/', function (req, res) {
  res.redirect('user router');
});
 
//user router
router.get('/user', userController.get_dummy);
router.post('/user', userController.post_dummy);
router.put('/user', userController.put_dummy);
router.delete('/user', userController.delete_dummy);
 
 
module.exports = router;

userModel.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
 
mongoose.createConnection('mongodb://localhost:27017/test');
 
var UserSchema = new Schema({
    name: String,
    address: String,
    phone: String,
    email: String
});
 
module.exports = mongoose.model('User', UserSchema);


'Framework > Nodejs' 카테고리의 다른 글

node js, mongodb 연동 with mongoose  (0) 2017.08.16
이메일 발송  (0) 2017.07.13
mocha  (0) 2017.07.05
미들웨어(1):cookieParser, bodyParser, limit, query  (0) 2017.04.05
connect  (0) 2017.04.04

mocha

mocha는 Javascript Testing Tool이다.

Installation

$ npm install mocha --global

테스트 코드는 .spec.js로 관리하여 -w를 추가하여 실행하여 파일 변경시 자동으로 테스트 러너가 실행된다

$ mocha

test

describe()으로 테스트 suite을 만들고 그 안에 it()으로 테스트 코드를 작성한다. descirbe()은 중첩해서 사용할 수 있다.

Asynchronous

it()에 사용하는 함수에 파라미터로 done을 전달하면 자동으로 비동기 테스트로 인식하고 비동기 로직이 완료된 시점에서 파라미터로 받은 done()을 실행해 주면 테스트가 완료된다. assertion은 done()을 실행하기 전에 작성하면 됩니다. done()을 실행하지 않으면 기본 타임아웃인 2000ms후에 타임아웃 실패로 간주합니다.

done() 이라는 콜백 함수를 비동기 코드가 정상적으로 완료되었을 때 호출하면 된다. done()이 호출되지 않는다면 설정된 기본 timeout 시간으로 2ms 후에 실패로 간주된다.

before(function(){
  console.log('before');
});
 
it('test spec', function(done){
  // do the test
  console.log(1);
  done();
});
 
after(function(){
  console.log('after');
});

mocha --timeout 3000 처럼 timeout 값을 변경할 수 있다.

Hooks

테스트 케이스의 상황에 따라 실행되어야 하는 부분을 설정할 때 사용

describe('Hooks example', function() {
  before(function() {
    // 테스터들을 실행하기 전에 한번 실행하는 부분
    console.log('before');
  });
 
  after(function() {
    // 테스터들을 모두 실행하고 나서 한번 실행하는 부분
    console.log('after');
  });
 
  beforeEach(function() {
    // 각 테스터들이 실행되기 전에 실행하는 부분
    console.log('beforeEach');
  });
 
  afterEach(function() {
    // 각 테스터들이 실행된 후에 실행하는 부분
    console.log('afterEach');
  });
 
  // test case
  it('test1', function() {
    // write test logic
    console.log('1');
  });
 
  it('test2', function() {
    // write test logic
    console.log('2');
  });
});
 

결과 화면

Hooks example
before
beforeEach
1
    ✓ test1
afterEach
beforeEach
2
    ✓ test2
afterEach
after
 
 
  2 passing (8ms)


'Framework > Nodejs' 카테고리의 다른 글

이메일 발송  (0) 2017.07.13
MVC model  (0) 2017.07.07
미들웨어(1):cookieParser, bodyParser, limit, query  (0) 2017.04.05
connect  (0) 2017.04.04
FileUpload  (0) 2017.03.26

쿠키와 요청본문, 쿼리 스트링을 해석하는 미들웨어

cookieParser()

웹 브라우저의 쿠키를 해석해 req.cookies에 넣는다.

var express = require('express');
var cookieParser = require('cookie-parser');
 
var app = express();
app.use(cookieParser());
 
app.get('/', function (req, res) {
  // Cookies that have not been signed
  console.log('Cookies: ', req.cookies);
 
  // Cookies that have been signed
  console.log('Signed Cookies: ', req.signedCookies);
})
 
app.listen(3000, function(req, res) {
  console.log("running on 3000 port");
});

cookie response setting

res.setHeader('Set-Cookie', 'foo=bar');
res.setHeader('Set-Cookie', 'tobi=feert');
res.end();

req.signedCookies(서명된 쿠키)

//req.signedCookies에 {name : value} 추가
res.cookie('name', 'value', {signed:true});
 
//req.cookies에 {name : value} 추가
res.cookie('name', 'value', {signed:false});

bodyParser()

요청 본문을 받아서 해석해 req.body에 넣는다.

var express = require('express')
var bodyParser = require('body-parser')
 
var app = express()
 
// create application/json parser
var jsonParser = bodyParser.json()
 
// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })

bodyParser.urlencodeed :
url 인코딩된 본문만을 parse하는 미들웨어를 반환, x-www-form-urlencoded parser 임을 명시
기본값은 true이지만 더이상 사용되지 않는다.

app.use(bodyParser.urlencoded({ extended: false }))

top-level middleware(모든 요청에 body 구문을 parse한다)

var express = require('express')
var bodyParser = require('body-parser')
 
var app = express()
 
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
 
// parse application/json
app.use(bodyParser.json())
 
app.use(function (req, res) {
  res.setHeader('Content-Type', 'text/plain')
  res.write('you posted:\n')
  res.end(JSON.stringify(req.body, null, 2))
})
 
app.listen(3000);

express4.x부터 body-parser 미들웨어의 multipart 기능 불가능해짐
connect-multipart 등 미들웨어 사용

limit()

bodyParser()와 같이 사용해서 요청이 커지지 않게 제한한다.

예시를 위해 bodyPaser 미들웨어에 multipart(업로드 및 form-data파싱)을 추가하가 위해 connect-multiparty 미들웨어를 사용

var fs = require('fs');
var express = require('express');
var multiparty = require('connect-multiparty');
var multipartMiddleware = multiparty();
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
 
var app = express();
 
app.use(cookieParser());
app.use(bodyParser.json({limit: '1mb'}));
//업로드하여 저장할 디렉토리 지정(디렉토리가 존재하여야 한다.)
app.use(multiparty({uploadDir : __dirname + '/multipart'}));
 
app.get('/', function(req, res) {
  fs.readFile('index.html', function(err, data) {
    res.send(data.toString());
  })
});
 
app.post('/', function(req, res) {
  console.log(req.body);
  console.log(req.files);
 
  res.redirect('/');
});
 
app.listen(3000, function() {
  console.log('server running on 3000 port');
})

limit가 필요한 이유 :
악의적인 사용자가 HTTP 클라이언트를 사용해 대량의 파일 데이터를 만들어서 공격하는 것을 방지

var express = require('express');
var http = require('http');
var app = express();
 
var req = http.request({
  method: 'POST',
  port:3000,
  headers: {
    'Content-Type': 'application/json'
  }
});
 
req.write('[');
var n = 30000000;
while(n--) {
  req.write('"foo",');
}
req.write('"bar"]');
 
req.end();

query()

요청 URL 쿼리 스트링을 req.query에 넣는다.

app.use(function (req, res) {
  res.setHeader('Content-Type', 'text/plain')
  res.write('you posted:\n')
  res.end(JSON.stringify(req.query));
})

localhost:3000?query=query_data 로 url 호출시 {"query":"query_data"}가 출력된다.

'Framework > Nodejs' 카테고리의 다른 글

이메일 발송  (0) 2017.07.13
MVC model  (0) 2017.07.07
mocha  (0) 2017.07.05
connect  (0) 2017.04.04
FileUpload  (0) 2017.03.26

+ Recent posts