error handler 와 router, middleware의 비교

node js 에서는 기본적으로 error handler 를 제공한다.

app.use((err, req, res, next) => {
 
})

그리고 err 인자가 없는 경우 미들웨어로 간주된다.

app.use((req, res, next) => {
 
})

그리고 아래의 경우에는 라우터로 간주된다. 경로와 http method가 지정되어 있다.

app.get('/', (req, res, next) => {
 
});

error handler 는 다른 미들웨어와 라우터 호출을 정의한 후 마지막으로 정의해야 한다.

즉, 순서에 영향을 받는다.

// router
app.get('/', (req, res, next) => {
  next();
});
 
// error handler
app.use((err, req, res, next) => {
 
});
 
app.listen(3000, function() {
 
});

next(err) 를 호출할 경우 중간에 등록된 미들웨어를 건너 뛰고 error handler 부분으로 건너가게 된다.

즉 next 인자로 new Error() 객체를 보내든 string 값을 보내면 에러로 감지하여 error handler로 보낸다.

// A router
app.use((req, res, next) => {
  // next(new Error('error message'))
  next('error message');
});
 
// B router - skip
app.use((req, res, next) => {
  console.log('B router');
});
 
// error handler
app.use((err, req, res, next) => {
  // print: error message
  console.log(err);
});

next() 혹은 next('route') 호출시 다음 미들웨어 혹은 라우터로 흐름을 넘긴다.

아래 예제는 /users/0 로 접근한 경우 next('route')를 통해 다음 라우터로 전달하게 된다.

/users/1 로 접근한 경우 next()를 통해 다음 미들웨어로 흐름을 넘겨 'next() call' 이 출력된다.

아래 예제는 router handler를 통해 하나의 경로에 2개의 route를 설정한 경우이다.

app.get('/users/:id', ( req, res, next) => {
  if(req.params.id == 0) next('route')
  else next()
}, (req, res, next) => {
  res.send('next() call')
});
 
app.get('/users/:id', ( req, res, next) => {
  res.send("next('route') call")
})

정리

error handler 에 대해 알아보았다. 개발을 하다보면 에러 처리를 하는 법이 아니라 어떻게 하는게 좋은지에 대한 best practice에 대해 궁금하게 된다.

블로그를 찾던 중 error handler에 대한 best practice 내용이 담겨 있다. 나중에 한번 보길 바란다.

https://www.joyent.com/node-js/production/design/errors

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

express-session  (0) 2018.01.03
pm2에 대해 알아보자  (0) 2017.10.06
[마이크로서비스] 세네카와 익스프레스 연동  (0) 2017.10.02
[마이크로서비스] Seneca  (0) 2017.10.02
비동기와 CPU Bound  (0) 2017.09.04

Session 이란?

서버가 해당 서버에 접근하는 클라이언트를 식별하는 방법 중 하나

서버는 접근하는 클라이언트에게 response-header field인 set-cookie 값으로 클라이언트 식별자인 session-id를 발행한다.

HTTP Session 동작 순서

클라이언트가 서버로 http 요청을 할 때, 서버에서 request-header field인 cookie를 확인해 해당 session-id를 보내왔는지 확인한다.

만약 없다면 session-id를 생성해 클라이언트에게 response-header field에 set-cookie 값으로 session-id를 발행한다.

이 때 발행된 쿠키는 메모리(브라우저)에 저장되며, 세션 종료시 같이 종료된다.

Node js에서 Session 관리

Node.js에서는 express-session이라는 모듈을 사용한다.

npm install --save express-session

우선 간단한 예제를 보자. 아래 예제는 express-session 미들웨어를 추가하는 예제이다.

  • secret : 세션 식별자를 쿠키에 저장하기 전에 암호화하기 위해 필요한 옵션

  • resave : 요청이 왔을 때 세션을 수정하지 않고 세션을 다시 저장소에 저장 여부

  • saveUninitialized : 초기화 되지 않은 세션을 강제로 저장.

const express = require('express');
const app = express();
const session = require('express-session');
 
app.use(session({
    secret: 'mySecret',
    resave: false,
    saveUninitialized: true
    // cookie: {secure: true }
}));

위에서 말했듯이 session을 사용하게 되면 브라우저에 세션에서 할당되는 session-id 값을 지닌 쿠키가 생성된다.



처음에는 저장된 쿠키가 없지만 아래의 코드를 추가시켜 보자. express-session 미들웨어를 추가시켰고 해당 미들웨어에서 세션에 대한 정보를 req.session으로 받는다.

app.get('/login', (req, res) => {
  var session = req.session;
 
  session.userId = 'test';
  session.userPw = '1234';
 
  res.json(req.session);
});

http://localhost:3000/login 으로 접속하여 보자.

쿠키가 생성이 되었음을 확인할 수 있다. connect.sid라는 이름으로 value는 secret 값이 추가되어 암호화된 임의의 문자열로 저장이 되어있을 것이다.


아래 코드를 추가시키고 /home으로 접속하면 저장된 쿠키를 통해 저장된 세션으로부터 userId와 userPw를 가져올 수 있다.

app.get('/home', (req, res) => {
  var session = req.session;
 
  res.json({
    userId: session.userId,
    userPw: session.userPw
  })
});
 

쿠키를 삭제 하고 싶다면 아래의 코드를 참조하자.

app.get('/delete', (req, res) => {
  req.session.destroy(function(err) {
    if(err) {
      res.end(err);
    }
 
    res.json({
      message: "success delete"
    })
  })
});

이외에도 cookie의 maxAge 를 변경할 수도 있다.

app.get('/maxage', (req, res) => {
  var hour = 3600000;
 
  req.session.cookie.expires = new Date(Date.now() + hour);
  req.session.cookie.maxAge = hour;
  req.session.userId = "test3";
 
  res.json(req.session);
});

정리


express-session을 사용하면 session 기반 인증 서버를 구현할 수 있다.

추가적으로 express-session npm site 에 접속하면 각 옵션들에 대한 자세한 정보가 있으니 확인해보면 좋을 것 같다.

곧 포스팅할 passport 역시 session을 기반으로 하기 때문에 session, cookie에 대한 이해를 위해 추가적으로 검색을 해보는 것도 좋을 것 같다.

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

error handler  (0) 2018.01.14
pm2에 대해 알아보자  (0) 2017.10.06
[마이크로서비스] 세네카와 익스프레스 연동  (0) 2017.10.02
[마이크로서비스] Seneca  (0) 2017.10.02
비동기와 CPU Bound  (0) 2017.09.04

pm2란?

  • pm2는 생산 프로세스 관리자로 서버 인스턴스들에 대한 로드 밸런싱과 더불어 Node.js의 스케일 업이나 스케일 다운을 돕는다.

  • 프로세스들이 계속 실행할 수 있는 환경을 제공한다.

  • 처리하지 못한 예외에 의해 쓰레드가 죽음으로 인해 어플리케이션이 죽는 현상을 방지한다.

  • 로드 밸러서 역할도 할 수 있다.

Node.js의 단일 쓰레드 어플리케이션과 예외

Node.js는 어플리케이션은 단일 쓰레드로 실행된다. 이것은 Node.js가 동시성을 지원하지 않는다는 의미가 아니다.(어플리케이션이 병렬로 실행된다는 것을 의미)

이는 다음을 암시한다.

예외가 처리되지 않으면 어플리케이션은 죽는다.

따라서 예외를 처리하기 위해 bluebird와 같은 promise library를 사용하여 성공과 실패에 대한 핸들러를 추가한다. 예외를 발생시키지 않음으로 어플리케이션이 죽는 경우를 방지한다.

하지만 처리할 수 없는 오류가 존재하기 때문에 pm2와 같은 태스크 러너를 사용하여 해결한다.

pm2 명령어

  1. app.js를 pm2에서 관리(app.js는 포크 모드에서 실행 - pm2가 로드 밸러서로 동작하지 않고 앱을 포크만 했다는 것을 의미)

pm2 start app.js

pm2 show [id] 을 통해 실행되고 있는 id에 해당하는 정보를 출력할 수 있다.

  1. pm2가 실행하는 앱의 개수를 모니터

pm2 monit
  1. logs를 확인

pm2 logs
  1. 어플리케이션 재시작 어플리케이션을 다운타임 없이 재시작을 보장한다.

pm2는 들어오는 요청을 큐에 대기시키고, 앱이 다시 반응하게 되면 앱의 재시작을 보장

pm2 reload all

pm2의 클러스터 기능

pm2는 클러스터 모드로 수행할 수 있는 기능을 제공한다. 클러스터 모드에서는 pm2는 지정된 대로 컨틀로러 프로세스와 많은 작업 프로세스를 생성한다. > Node.js 의 단일 쓰레드 기술로 멀티코어 CPU의 혜택을 누릴 수 있다.

  1. 실행중인 어플리케이션을 중지한다.

      pm2 stop all
  1. 클러스터 모드로 앱을 실행하기 위해 앱에 대한 정보를 지운다.

    pm2 delete all
  1. 다음 명령어는 세 개의 작업 프로세스 사이에서 라운드 로빈 방식으로 동작함을 의미한다. 동시에 3개의 요청에 대응할 수 있다.

    pm2 start app.js -i 3


다음 명령어를 통해 작업 프로세스의 수를 줄이거나 늘릴 수 있다.

    pm2 scale app 2

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

error handler  (0) 2018.01.14
express-session  (0) 2018.01.03
[마이크로서비스] 세네카와 익스프레스 연동  (0) 2017.10.02
[마이크로서비스] Seneca  (0) 2017.10.02
비동기와 CPU Bound  (0) 2017.09.04

세네카와 익스프레스 연동

세네카는 웹 프레임워크가 아니기에 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

+ Recent posts