Nodejs 프로그래밍

6. 경량 웹 프레임워크 익스프레스

               아키텍트를 꿈꾸는 사람들 오전반 스터디

                                 전효성( itmentor@gmail.com )
                                                         1
이번 발표에서 다룰 내용
1.   Express 프로젝트 셋팅
2.   app.js 소스 살펴보기
3.   Jade 뷰 템플릿 엔진
4.   폼 전송 웹사이트 예제
5.   데이터베이스 연동
6.   비동기 패턴의 의존성 문제



                       2
1. Express 프로젝트 셋팅
아래의 명령어를 순차적으로 입력합니다.
$ npm install express -g
$ express simpleweb
$ cd simpleweb
$ npm install
$ npm install express jade




                             3
Simpleweb 웹페이지 server-side구성
•   node_modules
     –   해당 웹프로젝트에서 사용하는 모듈들이 위치함.
•   Public
     –   정적 리소스 파일 저장 ( css, 이미지 등 )
•   Route
     –   url에 따라 호출될 함수를 모아두는 디렉토리.
•   Views
     –   클라이언트에 보여줄 화면의 템플릿이 위치함.
•   App.js
     –   main()함수 개념
•   Package.json
     –   npm으로 설치하면 여기에 설치된 모듈의 정보( 버전 )가 들어간다.




                                                  4
2. app.js 소스 살펴보기
// 모듈 종속성
var express = require('express')
  , routes = require('./routes');

var app = module.exports = express.createServer();

// configuration
app.configure( function() {
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');

  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});



                                                                              5
2. app.js 소스 살펴보기
// 모듈 종속성
var express = require('express')
  , routes = require('./routes');

var app = module.exports = express.createServer();
                                            현재 파일의 절대 경로
// configuration
app.configure( function() {
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');

  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});



                                                                              6
2. app.js 소스 살펴보기
// 모듈 종속성
var express = require('express')
  , routes = require('./routes');

var app = module.exports = express.createServer();

// configuration
app.configure( function() {
  app.set('views', __dirname + '/views');           • View template엔진과 view tempalte위치 지정
  app.set('view engine', 'jade');                   • app.set()  특정 키에 값을 지정

  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});



                                                                                            7
2. app.js 소스 살펴보기
// 모듈 종속성
var express = require('express')
  , routes = require('./routes');

var app = module.exports = express.createServer();

// configuration
app.configure( function() {
  app.set('views', __dirname + '/views');       • View template엔진과 view tempalte위치 지정
  app.set('view engine', 'jade');               • app.set()  특정 키에 값을 지정

  app.use(express.bodyParser());                           • app.use()  사용할 미들웨어 결정
  app.use(express.methodOverride());                       • express.static  리소스파일 위치 지정
  app.use(app.router);                                     • express.bodyParser()  application/x-www-form-
  app.use(express.static(__dirname + '/public'));            urlencoded나 application/json의 바디를 파싱하여
});                                                          req.body변수에 할당

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});



                                                                                                              8
2. app.js 소스 살펴보기
// 모듈 종속성
var express = require('express')
  , routes = require('./routes');

var app = module.exports = express.createServer();

// configuration
app.configure( function() {
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');

  app.use(express.bodyParser());                           • app.router  요청 url을 라우팅한다.
  app.use(express.methodOverride());
  app.use(app.router);                                     예, http://localhost:3000/id에 따라 페이지를 다르게 구성하고 싶
  app.use(express.static(__dirname + '/public'));          을 경우
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});



                                                                                                             9
2. app.js 소스 살펴보기
// 모듈 종속성
var express = require('express')
  , routes = require('./routes');

var app = module.exports = express.createServer();

// configuration
app.configure( function() {
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');

  app.use(express.bodyParser());                          • express.methodOverride()
  app.use(express.methodOverride());                      • <input>태그의 method를 오버라이드하여 브라우저
  app.use(app.router);                                      에서 지원 못하는 PUT, DELETE를 처리
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, HTTP명령어 })); 클라이언트의 의도
                                                       showStack: true
});
                                                     GET                            서버에서 정보를 얻어온다.
app.configure('production', function(){
                                                     POST                           정보를 서버에 보낸다.
  app.use(express.errorHandler());
});                                                  PUT                            기존에 존재하는 정보를 갱신한다.
                                                     DELETE                         특정 항목을 제거한다?!
                                                     •   참고 : http://code.google.com/intl/ko-KR/apis/gdata/docs/2.0/basics.html
                                                                                                                                  10
2. app.js 소스 살펴보기
// 모듈 종속성
var express = require('express')
  , routes = require('./routes');

var app = module.exports = express.createServer();

// configuration
app.configure( function() {
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');

  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));   개발 중일때 stack trace 출력
});

app.configure('production', function(){
  app.use(express.errorHandler());              실 서비스시 error를 출력하지 않음
});



                                                                                                      11
2. app.js 소스 살펴보기
// Routes – HTTP 명령어( get, put, post, delete )에 대한 이벤트 핸들러 연결
app.get('/', routes.index);

// 3000번 포트 오픈
app.listen(3000);

// 서버 콘솔에 메시지 출력
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);




simpleweb/route/index.js
/*
 * GET home page.
 */

exports.index = function(req, res){
  res.render('index', { title: 'Express' })
};
                                                                                                       12
3. Jade 뷰 템플릿 엔진




                   13
채수원님 블로그 : http://blog.doortts.com/223   14
HTML  Jade도 가능합니다.




    http://html2jade.aaron-powell.com/   15
4. 폼 전송 웹사이트 예제
            app.js                               route/index.js
app.get('/', routes.index);
app.get('/join', routes.form);              exports.index = function(req, res){
app.post('/join', routes.join)                res.render('index', { title: 'Express' })
                                            };

                                            exports.form = function(req,res) {
                                              res.render('join-form', { title: 'Express' });
                                            };

                                            exports.join = function(req,res) {
                                                             res.render('join-result', {
                                                                           username: req.body.name,
                                                                           useremail: req.body.email,
                                                                           title: 'Express'
                                                             });
                                            };



                                  views./layout.jade




            views./index.jade    views./join-result.jade                 views./join-form.jade
                                                                                                        16
5. 데이터베이스 연동 - mysql
  설치 및 테이블 생성
// install
$ npm install mysql

// TABLE생성
CREATE TABLE members (name VARCHAR(20), email VARCHAR(30));




     route/index.js                                           repository.js
var repo = require( ‘../repository’ );                   insertUser: function(user,res) {
                                                           ...
exports.index = function(req, res){                      }
…                                                        , hasNameAndEmail: function(user, res) {
                                                           client.query(
exports.join = function(req,res) {                           'SELECT * FROM ' + TABLE + 'WHERE name = ? OR email = ?'
  // repo.insertUser( req.body, res );                       , [user.name, user.email]
  repo. hasNameAndEmail( req.body, res );                    , function(err, results, fields) {
};                                                             if (err) {
                                                                 throw err;
                                                               }
                                                               if( results.length > 0 ) {
                                                                 res.render('join-fail', { title: 'Express' } );
                                                               } else {
                                                                 mysqlUtil.insertUser( user, res );
                                                               }
                                                           });
                                                         }                                                       17
5. 데이터베이스 연동 - mongodb
Repository.js




                       외부 인터페이스
                          DB접근 로직
                코드
                            가시화 로직
                구조




                뷰 렌더링 / DB접근 로직을 분리하자.


                                         18
6. 비동기 패턴의 의존성 문제

• 비동기로 수행된다  수행시점을 알 수 없
  다.
• 해당 시점에 실행될 함수를 function
  parameter로 전달


 var result = db.query(‘SELECT …’ ); //동기 방식 function call


 db.query(‘SELECT … ‘, 처리하는 함수) //비동기 방식 function call

                                                             19
6. 비동기 패턴의 의존성 문제 – 해결책

방법1. callback을 이용하여 의존성 분리
// a.js
var b = require( './b' );
b.funcA( function( err, result1 ) {
  b.funcB( result1, function( err, result2 ) {
    //result를 사용하는 코드 : 렌더링 로직
  });
});

// b.js
var B = module.exports = {
  funcA: function( callback ) {
    db.query( 'SELECT ... ', callback );
  },
  funcB: function( callbck ) {
    db.query( 'SELECT ... ', callback );
  }
}


                                                 20
6. 비동기 패턴의 의존성 문제 – 해결책

방법2. event를 이용하여 의존성 분리
// b.js                                                 // a.js
                                                        var b = require( './b' );
var EventEmitter = require('events').EventEmitter;      var resultA = b.funcA();
var B = module.exports = {
                                                        resultA.on( 'end', function( err, result ) {
  funcA: function() {
                                                          var resultB = b.funcB(result);
    var evt = new EventEmitter();
    db.query( 'SELECT ... ', function( err,result ) {     resultB.om( 'end', function( err, result ) {
      evt.emit('end', err, result );                        //result사용하는코드
    });                                                   });
    return evt;                                         });
  },
  funcA: function() {
    var evt = new EventEmitter();
    db.query( 'SELECT ... ', function( err,result ) {
      evt.emit('end', err, result );
    });
    return evt;
  }
}




                                                                                                         21
6. 비동기 패턴의 의존성 문제 – 해결책

비동기 호출 순서를 보장하는 방법
func repeater(i)
{
  if( i < length )
  {
      requestAsyncWork ( function() {
         repeater(i+1)
      })
   }
}

repeater(0)




    • 0번 요청 끝  1번 요청  1번 요청 끝  2번 요청 …
    • 결국 동기와 동일한 방식을 비동기로 구현한 것.




                                            22
7. 정리
• 웹서버 생성
 – express.createServer()
• 서버 설정
 – express.createServer().configure( callback )
 – Callback에서 express.createServer().set()과
   express.createServer().use()사용
• GET/POST의 라우팅
 – express.createServer().get()
 – express.createServer().post()
• HTML노가다 하지 말고 jade로 작성하자.
 – 디자이너와 소통이 어려울수도 있음.
                                                  23
질문 받겠습니다.


 Express에 대한 자세한 설명은 생략한
 다.
http://firejune.io/express/guide를 참조 한다.




                                           24

Nodejs express

  • 1.
    Nodejs 프로그래밍 6. 경량웹 프레임워크 익스프레스 아키텍트를 꿈꾸는 사람들 오전반 스터디 전효성( itmentor@gmail.com ) 1
  • 2.
    이번 발표에서 다룰내용 1. Express 프로젝트 셋팅 2. app.js 소스 살펴보기 3. Jade 뷰 템플릿 엔진 4. 폼 전송 웹사이트 예제 5. 데이터베이스 연동 6. 비동기 패턴의 의존성 문제 2
  • 3.
    1. Express 프로젝트셋팅 아래의 명령어를 순차적으로 입력합니다. $ npm install express -g $ express simpleweb $ cd simpleweb $ npm install $ npm install express jade 3
  • 4.
    Simpleweb 웹페이지 server-side구성 • node_modules – 해당 웹프로젝트에서 사용하는 모듈들이 위치함. • Public – 정적 리소스 파일 저장 ( css, 이미지 등 ) • Route – url에 따라 호출될 함수를 모아두는 디렉토리. • Views – 클라이언트에 보여줄 화면의 템플릿이 위치함. • App.js – main()함수 개념 • Package.json – npm으로 설치하면 여기에 설치된 모듈의 정보( 버전 )가 들어간다. 4
  • 5.
    2. app.js 소스살펴보기 // 모듈 종속성 var express = require('express') , routes = require('./routes'); var app = module.exports = express.createServer(); // configuration app.configure( function() { app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function(){ app.use(express.errorHandler()); }); 5
  • 6.
    2. app.js 소스살펴보기 // 모듈 종속성 var express = require('express') , routes = require('./routes'); var app = module.exports = express.createServer(); 현재 파일의 절대 경로 // configuration app.configure( function() { app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function(){ app.use(express.errorHandler()); }); 6
  • 7.
    2. app.js 소스살펴보기 // 모듈 종속성 var express = require('express') , routes = require('./routes'); var app = module.exports = express.createServer(); // configuration app.configure( function() { app.set('views', __dirname + '/views'); • View template엔진과 view tempalte위치 지정 app.set('view engine', 'jade'); • app.set()  특정 키에 값을 지정 app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function(){ app.use(express.errorHandler()); }); 7
  • 8.
    2. app.js 소스살펴보기 // 모듈 종속성 var express = require('express') , routes = require('./routes'); var app = module.exports = express.createServer(); // configuration app.configure( function() { app.set('views', __dirname + '/views'); • View template엔진과 view tempalte위치 지정 app.set('view engine', 'jade'); • app.set()  특정 키에 값을 지정 app.use(express.bodyParser()); • app.use()  사용할 미들웨어 결정 app.use(express.methodOverride()); • express.static  리소스파일 위치 지정 app.use(app.router); • express.bodyParser()  application/x-www-form- app.use(express.static(__dirname + '/public')); urlencoded나 application/json의 바디를 파싱하여 }); req.body변수에 할당 app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function(){ app.use(express.errorHandler()); }); 8
  • 9.
    2. app.js 소스살펴보기 // 모듈 종속성 var express = require('express') , routes = require('./routes'); var app = module.exports = express.createServer(); // configuration app.configure( function() { app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); • app.router  요청 url을 라우팅한다. app.use(express.methodOverride()); app.use(app.router); 예, http://localhost:3000/id에 따라 페이지를 다르게 구성하고 싶 app.use(express.static(__dirname + '/public')); 을 경우 }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function(){ app.use(express.errorHandler()); }); 9
  • 10.
    2. app.js 소스살펴보기 // 모듈 종속성 var express = require('express') , routes = require('./routes'); var app = module.exports = express.createServer(); // configuration app.configure( function() { app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); • express.methodOverride() app.use(express.methodOverride()); • <input>태그의 method를 오버라이드하여 브라우저 app.use(app.router); 에서 지원 못하는 PUT, DELETE를 처리 app.use(express.static(__dirname + '/public')); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, HTTP명령어 })); 클라이언트의 의도 showStack: true }); GET 서버에서 정보를 얻어온다. app.configure('production', function(){ POST 정보를 서버에 보낸다. app.use(express.errorHandler()); }); PUT 기존에 존재하는 정보를 갱신한다. DELETE 특정 항목을 제거한다?! • 참고 : http://code.google.com/intl/ko-KR/apis/gdata/docs/2.0/basics.html 10
  • 11.
    2. app.js 소스살펴보기 // 모듈 종속성 var express = require('express') , routes = require('./routes'); var app = module.exports = express.createServer(); // configuration app.configure( function() { app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 개발 중일때 stack trace 출력 }); app.configure('production', function(){ app.use(express.errorHandler()); 실 서비스시 error를 출력하지 않음 }); 11
  • 12.
    2. app.js 소스살펴보기 // Routes – HTTP 명령어( get, put, post, delete )에 대한 이벤트 핸들러 연결 app.get('/', routes.index); // 3000번 포트 오픈 app.listen(3000); // 서버 콘솔에 메시지 출력 console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env); simpleweb/route/index.js /* * GET home page. */ exports.index = function(req, res){ res.render('index', { title: 'Express' }) }; 12
  • 13.
    3. Jade 뷰템플릿 엔진 13
  • 14.
    채수원님 블로그 :http://blog.doortts.com/223 14
  • 15.
    HTML  Jade도가능합니다. http://html2jade.aaron-powell.com/ 15
  • 16.
    4. 폼 전송웹사이트 예제 app.js route/index.js app.get('/', routes.index); app.get('/join', routes.form); exports.index = function(req, res){ app.post('/join', routes.join) res.render('index', { title: 'Express' }) }; exports.form = function(req,res) { res.render('join-form', { title: 'Express' }); }; exports.join = function(req,res) { res.render('join-result', { username: req.body.name, useremail: req.body.email, title: 'Express' }); }; views./layout.jade views./index.jade views./join-result.jade views./join-form.jade 16
  • 17.
    5. 데이터베이스 연동- mysql 설치 및 테이블 생성 // install $ npm install mysql // TABLE생성 CREATE TABLE members (name VARCHAR(20), email VARCHAR(30)); route/index.js repository.js var repo = require( ‘../repository’ ); insertUser: function(user,res) { ... exports.index = function(req, res){ } … , hasNameAndEmail: function(user, res) { client.query( exports.join = function(req,res) { 'SELECT * FROM ' + TABLE + 'WHERE name = ? OR email = ?' // repo.insertUser( req.body, res ); , [user.name, user.email] repo. hasNameAndEmail( req.body, res ); , function(err, results, fields) { }; if (err) { throw err; } if( results.length > 0 ) { res.render('join-fail', { title: 'Express' } ); } else { mysqlUtil.insertUser( user, res ); } }); } 17
  • 18.
    5. 데이터베이스 연동- mongodb Repository.js 외부 인터페이스 DB접근 로직 코드 가시화 로직 구조 뷰 렌더링 / DB접근 로직을 분리하자. 18
  • 19.
    6. 비동기 패턴의의존성 문제 • 비동기로 수행된다  수행시점을 알 수 없 다. • 해당 시점에 실행될 함수를 function parameter로 전달 var result = db.query(‘SELECT …’ ); //동기 방식 function call db.query(‘SELECT … ‘, 처리하는 함수) //비동기 방식 function call 19
  • 20.
    6. 비동기 패턴의의존성 문제 – 해결책 방법1. callback을 이용하여 의존성 분리 // a.js var b = require( './b' ); b.funcA( function( err, result1 ) { b.funcB( result1, function( err, result2 ) { //result를 사용하는 코드 : 렌더링 로직 }); }); // b.js var B = module.exports = { funcA: function( callback ) { db.query( 'SELECT ... ', callback ); }, funcB: function( callbck ) { db.query( 'SELECT ... ', callback ); } } 20
  • 21.
    6. 비동기 패턴의의존성 문제 – 해결책 방법2. event를 이용하여 의존성 분리 // b.js // a.js var b = require( './b' ); var EventEmitter = require('events').EventEmitter; var resultA = b.funcA(); var B = module.exports = { resultA.on( 'end', function( err, result ) { funcA: function() { var resultB = b.funcB(result); var evt = new EventEmitter(); db.query( 'SELECT ... ', function( err,result ) { resultB.om( 'end', function( err, result ) { evt.emit('end', err, result ); //result사용하는코드 }); }); return evt; }); }, funcA: function() { var evt = new EventEmitter(); db.query( 'SELECT ... ', function( err,result ) { evt.emit('end', err, result ); }); return evt; } } 21
  • 22.
    6. 비동기 패턴의의존성 문제 – 해결책 비동기 호출 순서를 보장하는 방법 func repeater(i) { if( i < length ) { requestAsyncWork ( function() { repeater(i+1) }) } } repeater(0) • 0번 요청 끝  1번 요청  1번 요청 끝  2번 요청 … • 결국 동기와 동일한 방식을 비동기로 구현한 것. 22
  • 23.
    7. 정리 • 웹서버생성 – express.createServer() • 서버 설정 – express.createServer().configure( callback ) – Callback에서 express.createServer().set()과 express.createServer().use()사용 • GET/POST의 라우팅 – express.createServer().get() – express.createServer().post() • HTML노가다 하지 말고 jade로 작성하자. – 디자이너와 소통이 어려울수도 있음. 23
  • 24.
    질문 받겠습니다. Express에대한 자세한 설명은 생략한 다. http://firejune.io/express/guide를 참조 한다. 24