블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2014. 4. 25. 00:15 NodeJS/Concept

Node.js를 제대로 배워보고 싶다면 이렇게 시작해보자


1. Node.js 사용 영역

  - fast, real-time 어플리케이션 개발

  - scalable 확장가능성이 높은 어플리케이션 개발

  - data-driven modern web 어플리케이션 개발

  * 클라우드 환경 이해



2. 잘 못 된 코스 

  - 온라인 및 동영상 강좌들부터 보지 말기

  - 서점에서 서평들 읽고 책 고르지 말기 



3. 제대로 된 코스

  - 봐야할 리소스 

    + 초보 : The Node Beginner Book 번역본

    + Felix's NodeJS Guide 번역본

   + Node.js 소개 자료 -여름으로 가는 문

   + Mastering NodeJS (비젼미디어)


  - JavaScript 를 배운다 : JavaScript Graden 번역본


  - 설치 : Professional Node.js 1장 보고 개발환경 셋업

  - 이해 : Beginner Book 보고서 간단히 프로그램들 돌려보기 

  - 모듈 : CommonJS에 대하여 이해하기

  - Professional Node.js 3장부터 쭉 읽자. 그러면 서버단 개발에 대해서 이해할 수 있다

  - 우리 목적은 modern web application개발이니 front-end의 backbone.js 를 배운다 



4. Node.js & Backbone.js를 익혔다면 

  - http://dailyjs.com/web-app.html 여기서 다양한 어플리케이션의 모험을 해보시라

  - 더 나아가서는 템플릿을 위한 Handlebars.js 와 MongoDB를 익힌다


  * 2013.2.20 일에 작성한 블로그라서 backbone.js가 나왔다. 이제는 angular.js로 옮겨가는 분위기

    3주를 목표로 잡고 시작하시라~~~ Good Luck!



<참조>

  - 원문 : http://javascriptissexy.com/learn-node-js-completely-and-with-confidence/

posted by 윤영식
2013. 9. 23. 11:40 NodeJS/Concept

Node.js 위에 기본적으로 Express.js를 많이 사용하지만 좀 더 추상화 되고 MVC 다운 프레임워크를 사용해 보고자. Express.js 및 Socket.io 와 다양한 데이터베이스를 추상화해서 사용할 수 있는 기능을 갖춘 Sails.js Framework을 사용해 보도록 한다. 



1. 사용 배경

  - Enterprise 급 MVC Framework : Express.js + Socket.io 기본 설정되어 사용

  - Waterline 이라는 adapter를 통하여 다양한 Database의 호환성 보장 

    + 흐름 : Model 객체 <-> Waterline <-> Databases 

    + 지원 데이터베이스 : MySql, PostgreSql, MongoDB, etc

    + 데이터베이스 연결을 하지 않으면 파일 또는 디스크에 백만개 밑으로 key:value 저장하는 node-dirty의 어뎁터를 사용한다

  - 손쉬운 설치 및 RESTful Web Services 개발

  - socket.io 및 redis 통합 

  - SPA (Single Page Application) 개발을 위한 client framework (backbone, ember, angular)의 adapter 제공 

    + Yeoman generator 제공 : Generator Sails AngularJS



2. 설치 및 사용

  - Yeoman을 기본 설치한다 : Yeoman 이해와 설치하기

  - "generator-sails-angular" 설치 후 "yo" 명령수행 : 빨간색의 Sails-angular 선택 (주의, sails 0.9.4 버전과 호환안됨)

// generator 설치

$ sudo npm install -g generator-sails-angular generator-angular


// yeoman 명령 수행

$ yo

[?] What would you like to do? (Use arrow keys)

 ❯ Run the Angular generator (0.3.1)

   Run the Backbone generator (0.1.7)

   Run the Bootstrap generator (0.1.3)

   Run the Express generator (0.1.0)

   Run the Generator generator (0.2.0)

   Run the Mocha generator (0.1.1)

   Run the Sails-angular generator (0.0.1)

   Update your generators

   Install a generator

   Find some help

   Get me out of here!


// 선택을 하면 Server에 Sails 환경과 Client에 AngularJS 환경이 동시에 설정된다 

// Client는 기본 Twitter bootstrap을 사용하고 Client 추가 Component는 "bower install <Component명칭>" 을 사용하여 설치한다 

// Server는 "npm install <module명칭>"을 사용하여 설치한다


  - "sails lift" 수행 (2013.09.23현재 0.9.4 버전으로 sails upgrade : sudo npm update sails -g )

$ sails lift

info:

info:

info:    Sails.js           <|

info:    v0.9.4              |\

info:                       /|.\

info:                      / || \

info:                    ,'  |'  \

info:                 .-'.-==|/_--'

info:                 `--'-------'

info:    __---___--___---___--___---___--___

info:  ____---___--___---___--___---___--___-__

info:

info: Server lifted in `/Users/prototyping/sails/test`

info: To see your app, visit http://localhost:1337

info: To shut down Sails, press <CTRL> + C at any time.


debug: --------------------------------------------------------

debug: :: Mon Sep 23 2013 10:05:04 GMT+0900 (KST)

debug:

debug: Environment : development

debug: Port : 1337

debug: --------------------------------------------------------



3. Sails MVC 이해하기 

  - Model 

    + Waterline 이라는 Sails Adapter를 통하여 다양한 데이터베이스의 객체로 사용되어 진다 

    + 단, Waterline의 형식(JSON 포멧)에 맞추어서 Model을 정의한다 : 모든 데이터베이스에 추상화된 정의이다 

    + /config/adapters.js 파일에 Database 환경 설정을 한다 또한 관련 데이터 베이스 adapter를 설치해야 함 

       예) MongoDB 설치 

$ npm install sails-mongo --save

npm http 200 https://registry.npmjs.org/sails-mongo

npm http GET https://registry.npmjs.org/sails-mongo/-/sails-mongo-0.9.5.tgz

.. 중략 ..

    + /api/models/ 디렉토리 밑에 <Model>.js 파일이 위치한다

// model 생성  

$ sails generate model Person


// 생성 내역 : Model의 맨앞자는 자동으로 대문자로 변경됨 

$ /api/models> cat Person.js

/*---------------------

:: Person

-> model

---------------------*/

module.exports = {

attributes : {

// Simple attribute:

// name: 'STRING',


// Or for more flexibility:

// phoneNumber: {

// type: 'STRING',

// defaultValue: '555-555-5555'

// }

}

};/


// CRUD : 기본 Deferred Promised 지원 

생성 : Person.create

조회 : Person.findOne/find

갱신 : Person.update

제거 : Person.destroy



  - Controller

    + view와 model 사이의 제어 역할을 한다

    + /api/controllers/ 디렉토리에 위치함 

// comment controller 만들기 : 최종 명령 tag뒤에 스페이스가 있으면 안된다. 

// controller 뒤 첫번째 인자 : comment가 controller의 명칭

// controller 두번째 인자이후 : comment의 메소드 명칭들 열거  

$ sails generate controller comment create like tag 


// 결과 

$ /api/controllers> cat CommentController.js

/*---------------------

:: Comment

-> controller

---------------------*/

var CommentController = {

// To trigger this action locally, visit: `http://localhost:port/comment/create`

create: function (req,res) {

// This will render the view: /views/comment/create.ejs

res.view();

},


// To trigger this action locally, visit: `http://localhost:port/comment/like`

like: function (req,res) {

// This will render the view: /views/comment/like.ejs

res.view();

},


// To trigger this action locally, visit: `http://localhost:port/comment/tag`

tag: function (req,res) {

// This will render the view:/views/comment/tag.ejs

res.view();

}


};

module.exports = CommentController; 


// 브라우져 호출 

http://localhost:1337/comment/create (like, tag) 


  - Route

    + RESTful 호출을 uri를 제어하고 싶다면 Route를 지정한다 

    + /config/routes.js 파일안에 정의한다 

    + controller와 action을 정의한다 

    + home의 경우 

       1) /config/routes.js 에 하기와 같이 home 컨트롤러 정의 

       2) /api/controllers/HomeController.js 존재 : index액션(펑션) 존재

       3) /views/home/index.ejs 파일 존재 : 서버에서 rendering되어 클라이언트 응답 

// Routes

// *********************

// 

// This table routes urls to controllers/actions.

//

// If the URL is not specified here, the default route for a URL is:  /:controller/:action/:id

// where :controller, :action, and the :id request parameter are derived from the url

//

// If :action is not specified, Sails will redirect to the appropriate action 

// based on the HTTP verb: (using REST/Backbone conventions)

//

// action을 정의하지 않으면 다음의 기본 형식을 따른다 

// GET:     /:controller/read/:id

// POST:   /:controller/create

// PUT:      /:controller/update/:id

// DELETE:/:controller/destroy/:id

//

// If the requested controller/action doesn't exist:

//   - if a view exists ( /views/:controller/:action.ejs ), Sails will render that view

//   - if no view exists, but a model exists, Sails will automatically generate a 

//       JSON API for the model which matches :controller.

//   - if no view OR model exists, Sails will respond with a 404.

//

module.exports.routes = {

// To route the home page to the "index" action of the "home" controller:

'/' : {

controller : 'home'

}


// If you want to set up a route only for a particular HTTP method/verb 

// (GET, POST, PUT, DELETE) you can specify the verb before the path:

// 'post /signup': {

// controller : 'user',

// action : 'signup'

// }


// Keep in mind default routes exist for each of your controllers

// So if you have a UserController with an action called "juggle" 

// a route will be automatically exist mapping it to /user/juggle.

//

// Additionally, unless you override them, new controllers will have 

// create(), find(), findAll(), update(), and destroy() actions, 

// and routes will exist for them as follows:

/*


// Standard RESTful routing

// (if index is not defined, findAll will be used)

'get /user': {

controller : 'user',

action : 'index'

},

'get /user/:id': {

controller : 'user',

action : 'find'

},

'post /user': {

controller : 'user',

action : 'create'

},

'put /user/:id': {

controller : 'user',

action : 'update'

},

'delete /user/:id': {

controller : 'user',

action : 'destroy'

}

*/

};


  - View

    + Sails는 기본 ejs를 사용한다 

    + /views/ 밑에 위치한다 

    + Partial 파일을 include 할 수 있다 

    + 메인 파일 : layout.ejs 

   


  - Adapter

    + 데이터베이스를 바꾸고 싶다면 /config/adapters.js 에서 내용을 바꾼다 : MongoDB를 사용한다면 mongo adapter를 설치해야 한다

// Configure installed adapters

// If you define an attribute in your model definition, 

// it will override anything from this global config.

module.exports.adapters = {


// If you leave the adapter config unspecified 

// in a model definition, 'default' will be used.

'default': 'memory',

// In-memory adapter for DEVELOPMENT ONLY

// (data is NOT preserved when the server shuts down)

        // Sails-dirty는 Node-Dirty의 Adapter를 memory or disk에 JSON 을 저장하는 store이다. 백만개 밑으로 저장시 사용

memory: {

module: 'sails-dirty',

inMemory: true

},


// Persistent adapter for DEVELOPMENT ONLY

// (data IS preserved when the server shuts down)

// PLEASE NOTE: disk adapter not compatible with node v0.10.0 currently 

// because of limitations in node-dirty

// See https://github.com/felixge/node-dirty/issues/34

disk: {

module: 'sails-dirty',

filePath: './.tmp/dirty.db',

inMemory: false

},


// MySQL is the world's most popular relational database.

// Learn more: http://en.wikipedia.org/wiki/MySQL

mysql: {

module : 'sails-mysql',

host : 'YOUR_MYSQL_SERVER_HOSTNAME_OR_IP_ADDRESS',

user : 'YOUR_MYSQL_USER',

password : 'YOUR_MYSQL_PASSWORD',

database : 'YOUR_MYSQL_DB'

}

};

   + generator-sails-angular 를 yeoman통하여 설치하였다면 SPA를 개발하는 것이므로 실제로 ejs 쓸 일이 거의 없고, Client 단에서 AngularJS 정의에 따라 Routing과 View Control이 발생한다. 따라서 Controller를 통하여 수행할 action에서 response.view() 하는 것이 아니라, response.json(<JSON Object>) 만을 응답으로 주면 될 것으로 보인다. 또한 업무적인 처리는 action에서 구현하면 된다   

   + response.send(형식)

res.send(); // 204

res.send(new Buffer('wahoo'));

res.send({ some: 'json' });

res.send('<p>some html</p>');

res.send('Sorry, cant find that', 404);

res.send('text', { 'Content-Type': 'text/plain' }, 201);

res.send(404);

   + response.json(형식)

res.json(null);

res.json({ user: 'tj' });

res.json('oh noes!', 500);

res.json('I dont have that', 404);


* Redis나 데이터베이스의 외부 인터페이스 라이브러리는 Custom Adapter 개발을 수행한다. (만드는 방법



3. MongoDB 연결하여 RESTful 호출 테스트 

  - mongodb 환경설정 : sails-mongo 어뎁터가 설치되어 있어야 함 

$ cd config && vi adapters.js  이동하여 mongodb 설정 입력


////////////////////////

// adapters.js 수정 내역 

module.exports.adapters = {

  // If you leave the adapter config unspecified 

  // in a model definition, 'default' will be used.

  'default': 'mongo',


  // In-memory adapter for DEVELOPMENT ONLY

  memory: {

    module: 'sails-memory'

  },


  // Persistent adapter for DEVELOPMENT ONLY

  // (data IS preserved when the server shuts down)

  disk: {

    module: 'sails-disk'

  },


  // sails v.0.9.0

  mongo: {

    module   : 'sails-mongo',

    host     : 'localhost',

    port     : 27017,

    user     : '',

    password : '',

    database : 'sailsdb'

  }

};

  

  - Person model 수정

// ProjectName/models/Person.js  수정 내역 

module.exports = {

  attributes: {

  name: 'string',

  phone: 'string',

  address: 'string',

  sex: 'string',

  etc: 'string' 

  }

};


  - PersonController의 create 수정 : Promise 구문 형식 사용가능  

// ProjectName/controllers/PersonController.js  수정 내역

module.exports = {

  create: function (req,res) {

    Person.create({ 

      name: req.param('name'),

      phone: req.param('phone'),

      address: req.param('address'),

      sex: req.param('sex'),

      etc: req.param('etc')

    }).done(function(err, person){

      if(err) throw err;

      res.json(person);

    });

  },

  .. 중략 ..

}


  - Postman을 통하여 호출 테스트 : Postman chrome extension을 설치한다. 


  - mongo shell 에서 person collection 조회 : model이 mongodb에서 collection과 맵핑된다  

///////////////////

// mongo shell 확인 

mongo

MongoDB shell version: 2.4.5

connecting to: test

show dbs

local 0.078125GB

sailsdb 0.203125GB

use sailsdb

switched to db sailsdb

show collections

person

system.indexes

> db.person.find();

{ "name" : "도원", "phone" : "1004", "address" : "seoul", "sex" : "man", "etc" : "developer", "createdAt" : ISODate("2013-09-23T02:28:51.281Z"), "updatedAt" : ISODate("2013-09-23T02:28:51.281Z"), "_id" : ObjectId("523fa76377e9f89562000001") }



4. MongoDB의 _id를 sequence number로 바꾸기 

  - "_id" : ObjectId("523fa76377e9f89562000001") 라고 나오는 것을 "_id" : 1 씩 증가하는 정수로 바꾸기 

  - 최대한 sails-mongo adapter의 api를 사용하여 구현한다 

  - sequence document 만들기 

> db.seq.insert(

 {

   _id: 'personid',

   seq: 0

 });

> db.seq.find();

{ "_id" : "personid", "seq" : 0 }


  - Sails Model을 정의한다 

$ sails generate model seq

$ cd models && vi Seq.js


// Seq.js 내역 

// _id를 통하여 여러 model의 sequence 값을 구분하기 위해 사용한다 

module.exports = {

  attributes: {

  _id: 'string',

  seq: 'integer'

  }

};


  - Person의 모델에서 명시적으로 _id 를 integer로 정의한다 

module.exports = {

  attributes: {

  _id: 'integer',

  name: 'string',

  phone: 'string',

  address: 'string',

  sex: 'string',

  etc: 'string'

  }

};


  - PersonController.js 내역 수정 : Gist 소스 파일

  /**

   * /person/create

   */ 

  create: function (req,res) {

      // seq 번호를 얻어온다 

      Seq.find({_id: 'personid'}).done(function(err, seqObj) {

        if(err) throw err;

        // 배열임을 주의  

        var seqNo = seqObj[0].seq;

        seqNo++;

        console.log('>>> seqNo is', seqNo);

        

        // insert시에 _id 값을 넣어준다 

        Person.create({ 

          _id: seqNo, 

          name: req.param('name'),

          phone: req.param('phone'),

          address: req.param('address'),

          sex: req.param('sex'),

          etc: req.param('etc')

        }).done(function(err, person){

          if(err) throw err;

          res.json(person);

        });


       // sails-mongo의 api에 한번에 select & update해주는 findAndModify 메소드가 없는 관계로 seqNo를 update해준다 

        Seq.update({_id: 'personid'}, {seq: seqNo}).done(function(err, seqObj){

          if(err) throw err;

        })

      });

  },


  - Postman으로 테스트 데이터를 입력한다 


  - mongo shell로 mongodb에 저장된 내역을 확인해 보자 

// postman통하여 데이터를 2개 넣었을 경우 

> db.seq.find();

{ "_id" : "personid", "seq" : 2, "updatedAt" : ISODate("2013-09-23T09:47:29.548Z") }


> db.person.find();

{ "_id" : 1, "name" : "윤도원-8", "phone" : "1004", "address" : "목동", "sex" : "사람", "etc" : "모비콘 블로깅", "createdAt" : ISODate("2013-09-23T09:45:36.391Z"), "updatedAt" : ISODate("2013-09-23T09:45:36.391Z") }

{ "_id" : 2, "name" : "윤도원-9", "phone" : "1004", "address" : "목동", "sex" : "사람", "etc" : "모비콘 블로깅", "createdAt" : ISODate("2013-09-23T09:47:29.547Z"), "updatedAt" : ISODate("2013-09-23T09:47:29.547Z") }

>

 

 

5. Sails를 통항 WebApp 만들기

  - SailsCast Site 참조

  - SailsCast GitHub 소스

  - 유튜브 동영상 25 강좌 보기


 

<참조>

  - Sails.js + AngularJS + MongoDB 데모

  - MongoDB auto increment Sequence Field 방법

  - Mongoose Sequence Table 통한 auto increment plugin  : express 와 mongoose 사용시 해당 plugin을 사용

posted by 윤영식
2013. 8. 14. 14:32 NodeJS/Modules

Node.js 프로젝트에서 사용된 자신의 모듈을 공유하고 싶다면 NPM Regsitry를 이용하면 된다. Publis Registry에 등록하고 사용하는 방법에 대해 알아보자 



1. NPM Registry

  - NPM Registry는 누구나 Node.js에서 사용할 수 있는 모듈을 일정한 양식만 갖춘다면 등록할 수 있고,  

    npm 명령을 통하여 Module Registry 에서 필요한 모듈을 설치할 수 있다

  - 홈페이지 : https://npmjs.org/

  - 홈페이지에서 계정을 미리 생성해 놓자 : 자신이 등록한 모듈 목록을 볼 수 있다

    




2. 배포전 준비하기 

  - .npmignore 파일 : npm 저장소에 배포하지 않을 것들에 대해 열거한다. 해당 파일이 없을 경우 .gitignore를 사용한다 

// .gitignore 내역 

.DS_Store

.git*

node_modules/

  - 모듈이 이미 GitHub에 등록되고 npm init을 통하여 package.json 파일 내역이 정확히 장성되었음을 가정한다 (참조)

// package.json 내역 

// 주의)

// npm init으로 해당 파일 생성시 engines 내역은 포함되지 않는 관계로 사용하는 Node.js 버전을 명시하여 준다 

// name : 값은 대문자와 띄워쓰기 없이 소문자로 이어서 작성한다 

// version : semantic versioning을 사용한다 major.minor.patch

// main : 프로그램 진입점

// test : 테스트 프로그램 수행 스크립트 명령 (npm test)

{

  "name": "mobiconstatistics",

  "version": "0.0.1",

  "description": "include statistics function such as the moving average",

  "main": "./lib/mobiconStatistics.js",

  "engines": { "node": ">= 0.10.0" }, 

  "dependencies": {

    "underscore": "~1.5.1"

  },

  "devDependencies": { 

    "mocha": "latest",

    "should": "~1.2.2"

  },

  "scripts": {

    "test": "mocha test/*.js"

  },

  "repository": {

    "type": "git",

    "url": "https://github.com/ysyun/mobiconStatistics.git"

  },

  "keywords": [

    "mobicon",

    "statistics",

    "movingAverage"

  ],

  "author": "yun youngsik",

  "license": "MIT",

  "bugs": {

    "url": "https://github.com/ysyun/mobiconStatistics/issues"

  }

}




3. 배포하기 

  - https://www.npmjs.org/ 사이트에 가입을 한다.

  - 모듈 디렉토리로 이동하고 사용자를 추가합니다 : npm adduser 

  - 가입했던 username과 password등을 입력하면 된다.

Users/development/node-modules/mobiconStatistics> npm adduser

Username: (ysyun)

Password: 

Email: 

  - 모듈 배포전 npm install이 잘 되는지 테스트 합니다 

    test 폴더를 하나 만들어서 기존에 만든 모듈을 install 해봅니다. test 폴더에 node_modules/mobiconstatistics 모듈을 설치되면 성공!

/Users/development/node-modules/mobiconStatistics> mkdir ../mobiconStatistics-installTest

/Users/development/node-modules/mobiconStatistics> cd ../mobiconStatistics-installTest

/Users/development/node-modules/mobiconStatistics-installTest> npm install ../mobiconStatistics

npm http GET https://registry.npmjs.org/underscore

npm http 304 https://registry.npmjs.org/underscore

mobiconstatistics@0.0.1 node_modules/mobiconstatistics

└── underscore@1.5.1


/Users//development/node-modules/mobiconStatistics-installTest/node_modules/mobiconstatistics> ls

-rw-r--r--  1   staff  1096  8 13 17:09 LICENSE

drwxr-xr-x  3   staff   102  8 13 17:31 test

drwxr-xr-x  3   staff   102  8 13 17:41 lib

-rw-r--r--  1   staff    49  8 14 11:26 .travis.yml

-rw-r--r--  1   staff   904  8 14 11:34 README.md

-rw-r--r--  1   staff  1881  8 14 14:19 package.json

drwxr-xr-x  3   staff   102  8 14 14:19 node_modules

  - 모듈을 배포합니다 : npm publish

Users//development/node-modules/mobiconStatistics> npm publish                          

npm http PUT https://registry.npmjs.org/mobiconstatistics

npm http 201 https://registry.npmjs.org/mobiconstatistics

npm http GET https://registry.npmjs.org/mobiconstatistics

npm http 200 https://registry.npmjs.org/mobiconstatistics

npm http PUT https://registry.npmjs.org/mobiconstatistics/-/mobiconstatistics-0.0.1.tgz/-rev/1-b8e34dc09489f8c4c9ece305d250264a

npm http 201 https://registry.npmjs.org/mobiconstatistics/-/mobiconstatistics-0.0.1.tgz/-rev/1-b8e34dc09489f8c4c9ece305d250264a

npm http PUT https://registry.npmjs.org/mobiconstatistics/0.0.1/-tag/latest

npm http 201 https://registry.npmjs.org/mobiconstatistics/0.0.1/-tag/latest

+ mobiconstatistics@0.0.1

  - 모듈이 잘 배포되었는지 확인합니다 : http://npmjs.org/package/<모듈명>

    예) https://npmjs.org/package/mobiconstatistics

    




4. 모듈 사용하기 

  - 이제 npm registry로 자신의 모듈이 배포되었으므로 프로젝트에 첨부하여 사용할 수 있게 되었습니다. 

// mobiconstatistics 모듈 추가하기 

/Users/development/smart-solutions/SmartStatistics> npm install mobiconstatistics --save

npm http GET https://registry.npmjs.org/mobiconstatistics

npm http 200 https://registry.npmjs.org/mobiconstatistics

mobiconstatistics@0.0.1 node_modules/mobiconstatistics


// 프로젝트의 package.json에 mobiconstatistics 모듈 추가

/Users/development/smart-solutions/SmartStatistics> cat package.json

{

  "name": "smartstatistics",

  "version": "0.0.0",

  "dependencies": {

    "sails": "0.8.9",

    "express": "~3.2.6",

    "ejs": "~0.8.4",

    "underscore": "~1.5.1",

    "mobiconstatistics": "0.0.1"

  },

  - 트위터에서 npm registry에 배포된 모듈중 괜찮은 것들을 정리하여 트윗을 해준다 : @nodenpm

    



<참조>

  - Node.js 소개 in opentutorial

  - npm 이란? 

  - Node Module NPM Registry 등록하기

  - npm developer 가이드

posted by 윤영식
2013. 7. 20. 12:25 NodeJS/Concept

Smart Dashboard라는 장난감을 하나 만들면서 서버단을 Node.js로 코딩을 하고 있다. Node.js에서 가장 햇갈리는 부분 그리고 인식의 전환을 해야하는 것들을 "Art of Node"에서 잘 정리해 주고 있다. Node.js의 핵심은 무엇인지 알아보자.



1. Callback

  - Node.js 는 I/O Platform 이다 : filesystm과 network I/O를 위한 플랫폼이다. Gateway와 같다고 생각해 보자~~ 


  


  - Callback은 node.js에서 나온 개념이 아니라 이미 있던 개념 : C언어에서 "함수포인터"와 같다 

  - Async가 기본이기 때문에 결과에 대한 처리를 요청하는 Callback function을 파라미터로 넘긴다 

  - 다음 예는 undefined가 나온다. Async이기 때문에 addOne() 호출(invoke)하고 바로 console.log(myNumber)으로 이동해 버린다. File I/O보다 Memory I/O가 훨씬 더 빠른다. 

var fs = require('fs') // require is a special function provided by node
var myNumber = undefined // we don't know what the number is yet since it is stored in a file

function addOne() {
  fs.readFile('number.txt', function doneReading(err, fileContents) {
    myNumber = parseInt(fileContents)
    myNumber++
  })
}

addOne()

console.log(myNumber) // logs out undefined -- this line gets run before readFile is done

  - 다음 예가 정확한 예이고 Closure개념을 결합하여 정보를 받아서 출력하고 있다. callback을 addOne 함수의 파라미터로 넘겨서 fs.readFile내부의 doneReading Callback 펑션에서 Closure객체로 callback()을 호출하는 것이다

var fs = require('fs')
var myNumber = undefined

function addOne(callback) {
  fs.readFile('number.txt', function doneReading(err, fileContents) {
    myNumber = parseInt(fileContents)
    myNumber++
    callback()
  })
}

function logMyNumber() {
  console.log(myNumber)
}

addOne(logMyNumber)

  - 이것에 대한 Pseudo Pattern 정리하면 다음과 같다 

function addOne(thenRunThisFunction) {
  waitAMinute(function waitedAMinute() {
    thenRunThisFunction()
  })
}

addOne(function thisGetsRunAfterAddOneFinishes() {})

  - Callback을 하다보면 간혹 이런 가독성이 떨어지는 문구를 볼 수 있다. 이에 대한 해결은 Defer/Promise 를 사용한다. Java 진영에서는 Future라고 한다. 주로 Async 프로그래밍시 Callback을 사용할 때의 문제점을 serial하게 해결할 수 있는 do -> then -> error 처리가 가능함 

a(function() {
  b(function() {
    c()
  })
})



2. Events

  - Node.js는 Even Machine (about ruby on rails or twisted of Python) 이다 

  - Observer Pattern을 사용하고, Publish/Subscribe 개념을 갖는다 

  - on 펑션을 통하여 Subscribe 하고, emit 펑션을 통하여 Publish 한다

  - 하기 예는 Subscribe하여 처리할 Handler를 Callback 펑션 정의(Definition)을 미리한다. 그리고 connect() 펑션에 인자로 넣는다 

var chatClient = require('my-chat-client')

function onConnect() {
  // have the UI show we are connected
}

function onConnectionError(error) {
  // show error to the user
}

function onDisconnect() {
 // tell user that they have been disconnected
}

function onMessage(message) {
 // show the chat room message in the UI
}

chatClient.connect(
  'http://mychatserver.com',
  onConnect,
  onConnectionError,
  onDisconnect,
  onMessage
)

  - 위 예를 다른 형태로 변경하면, 즉, connect() 메소드에서 작용하는 원칙은 다음과 같다. on(<EventName>, <Callback Function>) 를 통하여 Subscribe하는 것과 동일하다 

var chatClient = require('my-chat-client').connect()

chatClient.on('connect', function() {
  // have the UI show we are connected
}) 

chatClient.on('connectionError', function() {
  // show error to the user
})

chatClient.on('disconnect', function() {
  // tell user that they have been disconnected
})

chatClient.on('message', function() {
  // show the chat room message in the UI
})

  


3. Stream

  - Stream I/O 를 통하여 끊김없이 file system, network I/O를 수행할 수 있다. 성능의 병목없이 물흐르듯 데이터가 흘러간다. 

  - Substack사의 Stream Handbook을 보자

  - stream module은 노드의 코어모듈이다 : require('stream')

  - 하기 코드는 data.txt 파일읽는 것이 다 끝나면 Callback의 data로 전달이 된다. 이때 data.txt파일이 크다면 그만틈의 메모리를 사용하게 되고 병목이 생길 수 있다. Async 방식을 고려한다면 성능상의 문제를 야기할 수 있겠다. data.txt파일 읽으면서 http응답 병목발생. 

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
    fs.readFile(__dirname + '/data.txt', function (err, data) {
        res.end(data);
    });
});
server.listen(8000);
  - 위의 예제를 Stream 방식으로 변경한다. 파일을 읽어서 일정 크기단위(Chunk)로 요청한 Client로 응답을 준다.  
var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
    var stream = fs.createReadStream(__dirname + '/data.txt');
    stream.pipe(res);
});
server.listen(8000);
  - .pipe() 의미

.pipe() is just a function that takes a readable source stream src and hooks the output to a destination writable stream dst: - 읽을 소스 스트림 src가 있고 쓸 스트림으로 output을 연결해 주는 펑션일 뿐이다

src.pipe(dst)

  - 스트림 체이닝(Chaining)의 표현
a.pipe(b).pipe(c).pipe(d)

위 아래 동일 표현

a.pipe(b);
b.pipe(c);
c.pipe(d);

the command-line to pipe programs

a | b | c | d 

  - 스트림의 5가지 : readable, writable, transform, duplex, classic
  - Readable : pipe의 읽을 소스에 대한 처리를 별도로 해주어 output destination으로 보낸다. readableStream.pipe(dst) 
var Readable = require('stream').Readable;

var rs = new Readable;
rs.push('beep ');
rs.push('boop\n');
rs.push(null);

rs.pipe(process.stdout);
$ node read0.js
beep boop
  - Writable : 소스를 읽고서 표현하는 output에 대하여 별도로 지정한다. src.pipe(writableStream)
var Writable = require('stream').Writable;
var ws = Writable();
ws._write = function (chunk, enc, next) {
    console.dir(chunk);
    next();
};

process.stdin.pipe(ws);
$ (echo beep; sleep 1; echo boop) | node write0.js 
<Buffer 62 65 65 70 0a>
<Buffer 62 6f 6f 70 0a>
  - classic방식은 node v0.4 버전때 처음 나왔다. 
  - duplex는 a.pipe(b).pipe(a) 와 같이 양방향 파이프
  - transform은 read/write filter 이다

 


4. Modules

  - 모듈개념으로 npm(Node Package Manager)를 통하여 필요한 것을 설치한다 : 34,000 개가량의 모듈이 등록되어 있음

  - 모듈 찾기 npm search <ModuleName> : npm search pdf

  - package.json 안에 해당 모듈이 사용하는 다른 모듈 정보와 모듈의 일반 정보(이름, 버전, 저장소등)가 있다 : npm init

  - node코드에서 require('some-module'); 를 찾는다. require 동작 순서

  • if a file called some_module.js exists in the current folder node will load that, otherwise - 현재 디렉토리에 있는지 찾는다 
  • node looks in the current folder for a node_modules folder with a some_module folder in it - 현재 디렉토리에 node_modules 디렉토리가 있는지 찾고 있으면 그 밑으로 some_module 폴더가 있는지 찾는다  
  • if it doesn't find it, it will go up one folder and repeat step 2 - 위 두개다 찾지 못하면 윗쪽 폴더로 가서 두번째 처럼 다시 node_module 폴더를 찾아간다 


  • <참조>

      - art of node 원문

      - 함수 포인터에 대한 이해

      - Closure 알아가기

      - NHN이 이야기하는 함수형 언어와 클로져 이야기

      - Substack사의 Stream Handbook

    posted by 윤영식
    2013. 5. 7. 09:37 NodeJS/Concept

    서비스 또는 솔루션을 만들기 위해 Node.js와 Express.js를 선택하였다면  그 다음 고민은 애플리케이션 개발을 위하여 필요한 스택을 선정하는 일이다. 크게는 로깅, 환경설정, 통신등 기본적인 부분들을 직접 개발하지 말고 이미 만들어진 바퀴를 공짜로 구해서 달아보자 



    1. 준비하기 

      - Programming Javascript Application Book 책의 내용을 발췌한 것이다 

      - Node 버전관리 NVM 설치 

        + https://github.com/creationix/nvm

        + 설치 : nvm install [버전]

        + 수행 : nvm run [버전]

        + 확인 : nvm ls  (설치된 버전 목록을 보여줌)

        + 가능 : nvm ls-remote (설치 가능 버전 목록을 보여줌)

      - nvm 설치 명령 : git 사전 설치요구 : 수행시 nvm.sh 에 syntax오류가 나오면 nvm.sh을 copy하고 기존것 삭제후 다시 paste하여 만듦

    $ curl https://raw.github.com/creationix/nvm/master/install.sh | sh


        + 기타 명령들 

    $ nvm help


    Node Version Manager


    Usage:

        nvm help                          Show this message

        nvm install [-s] <version>  Download and install a <version>

        nvm uninstall <version>     Uninstall a version

        nvm use <version>           Modify PATH to use <version>

        nvm run <version> [<args>]  Run <version> with <args> as arguments

        nvm ls                            List installed versions

        nvm ls <version>             List versions matching a given description

        nvm ls-remote                 List remote versions available for install

        nvm deactivate                Undo effects of NVM on current shell

        nvm alias [<pattern>]      Show all aliases beginning with <pattern>

        nvm alias <name> <version>    Set an alias named <name> pointing to <version>

        nvm unalias <name>                Deletes the alias named <name>

        nvm copy-packages <version> Install global NPM packages contained in <version> to current version


    Example:

        nvm install v0.4.12            Install a specific version number

        nvm use 0.2                    Use the latest available 0.2.x release

        nvm run 0.4.12 myApp.js   Run myApp.js using node v0.4.12

        nvm alias default 0.4        Auto use the latest installed v0.4.x version


      - 프로젝트 만들고 초기화 하기 

        + 디렉토리 만들고 해당 디렉토리로 이동

        + npm init 수행하여 package.json 파일 만들기 : 이름, 버전, 설명, 키워드, 저장소위치, 라이센스 및 기타 정보등을 입력한다 

    $ mkdir basic && cd basic

    $ npm init

    This utility will walk you through creating a package.json file.

    It only covers the most common items, and tries to guess sane defaults.


    See `npm help json` for definitive documentation on these fields

    and exactly what they do.


    Use `npm install <pkg> --save` afterwards to install a package and

    save it as a dependency in the package.json file.


    Press ^C at any time to quit.

    name: (basic) JavaScript-Programming

    version: (0.0.0) 0.0.1

    description: This is a javascript programming

    entry point: (index.js)

    test command: grunt test

    git repository: https:/github.com/ysyun/javascriptacular

    keywords: angularjs

    author: yun dowon

    license: (BSD) MIT

    About to write to /Users/prototyping/express/basic/package.json:


    {

      "name": "JavaScript-Programming",

      "version": "0.0.1",

      "description": "This is a javascript programming ",

      "main": "index.js",

      "scripts": {

        "test": "grunt test"

      },

      "repository": {

        "type": "git",

        "url": "https:/github.com/ysyun/javascriptacular"

      },

      "keywords": [

        "angularjs"

      ],

      "author": "yun dowon",

      "license": "MIT"

    }



    Is this ok? (yes)

    npm WARN package.json JavaScript-Programming@0.0.1 No README.md file found!

    $ ls -lart

    -rw-r--r--  1 nulpulum  staff  360  5  7 09:22 package.json

    drwxr-xr-x  3 nulpulum  staff  102  5  7 09:22 .


      - 모듈 설치시에 package.json 파일에 자동 기록을 위하여 npm install --save [module] 처럼 --save 옵션을 준다 



    2. 준비할 스택들

    • Mout Like Underscore / LoDash. Stuff that should probably be included in JavaScript.

    • Express Web application framework.

    • Nconf Application config.

    • Hogan Mustache for express. (Jade 또는 EJS 를 써도 됨)

    • Superagent Communicate with APIs.

    • Socket.io Realtime communications (websockets).

    • Q Promises.

    • Async Asynchronous functional utilities.

    • Bunyan Logging. (Winston도 많이 사용)

    • Tape Testing. (Mocha, Karma 도 사용)

    • Cuid Better than guid/uuid for web applications.

    • Derby.js Add realtime, collaborative MVC to express. (Sails.js 또는 Bone.js 도 사용)

    • Node-http-proxy Proxy your service APIs.



    3. Express 사용하기 

      - Routing 을 이용한다

      - Middleware를 통하여 중간에 Hooking 하여 처리하고 다음으로 next() 호출 넘기는 use 를 사용한다 

    // Add some data to the request object that your other
    // midleware and routes can use.
    app.use(function (req, res, next) {
      req.foo = 'bar';
      next();

    });

        + 전체 예제 

    'use strict';
    var express = require('express'),
     
      // Create app instance.
      app = express(),
     
      // Use the `PORT` environment variable, or port 44444
      port = process.env.PORT || 44444;
     
    // The new middleware adds the property `foo` to the request
    // object and sets it to 'bar'.
    app.use(function (req, res, next) {
      req.foo = 'bar';
      next();
    });
     
    app.get('/', function (req, res) {
      res.setHeader('Content-Type', 'text/plain');
     
      // Send the value passed from the middleware, above.
      res.end(req.foo);
    });
     
    app.listen(port, function () {
      console.log('Listening on port ' + port);

    });


    // 결과 : use를 설정한 순서대로 처리된다 

    $ curl http://localhost:44444/
    bar



    4. SailsJS

      - 난 sails를 Full Stack Server Side Data-Oriented API Framework 이라고 말하고 싶다.

      - Front-end 단이 SPA로 개발될 경우

      - Back-end를 API 서버로 확장함 

      - Node.js + Express.js + MongoDB 와 연결 및 JSON Memory DB 사용가능

        + sails-angularjs-yeoman 의 로그인 데모

        + sails-mongodb 연결

        + sails-redis 연결



    <참조>

       - 원문 : Getting start with Node and Express 

       - middleware의 next() 사용법 : Node.js의 connect 이해하기

       - AngularJS와 MongoDB 연결 Bridge


    posted by 윤영식
    2013. 3. 22. 10:38 NodeJS/Concept

    Node.js를 이용하여 RESTful Web Services를 작성할 때 Open API에 대한 기본적인 인식을 가지고 시작하는 것이 좋겠다. Open API 를 만들기 위한 기본적인 원칙을 알아보자 



    1) Open API 배우기  

    KTH에서 Baas.io 라는 클라우드 서비스를 런칭하였다. 지인의 추천으로 써볼려고 들어갔는데, powered by Apigee 라는 문구가 낯설었다. apigee에서 이야기하는 Open API 기본 원칙을 보자

      - apigee에 대한 기본 생각 : 도움되는 사이트를 링크함 

      - apigee에서 발간한 Free eBook중 Web API Design 원칙 (한글 의역)


        + 기본 URL에는 동사가 아니라 명사를 사용한다 (단, 계산, 번역, 전환관련 부분은 동사씀)

        + 복수(컬렉션)을 먼저 나오게함 : /dog -> /dogs 처럼

        + 컬렉션뒤에는 개별적인 요소아이디(Elment ID) 가 온다 : /dogs/1234

        + 그외 복잡한 것은 ? 뒤로 두어라 : /dogs/1234?fields=name,age,location

        + 오류를 명확히 처리해라 : code, message, more_info, status 등으로 여기서 code는 HTTP code로 보냄

        + 버전관리를 한다 : /v1/dogs/1234 즉, 컬렉션 앞에 개들의 버전을 두는 것이다

        + 페이징 처리를 한다 : /v1/dogs?limit=25&offset=50  즉, limit, offset 을 키로 한다 

        + 다양한 포멧팅일 경우 . (도트) 사용한다 : /v1/dogs.json 그러나 json 포멧으로 통일해라 

        + 검색은 q 키를 사용 : /search?q=1234

        + 하위 도메인은 : api.xxx.com, developers.xxx.com 으로 api, developers 로 한다 

        + 권한 관리는 OAuth2.0 으로 통일

        + API Facade Pattern 을 지향한다 


      - 유명한 사이트들의 OpenAPI를 검색할 수 있다 : 페이스북, 트위터, GitHub, Google 서비스들 



    2) apigee에서 이야기하는 RESTful API Design Guide


    Web2.0 에 따라 많은 서비스들이 SPA 로 개발되고 Open API화 하면서 이러한 API를 모아서 관리할 수 있는 apigee 와 같은 스타트업이 생겨나고 있다. 큰 사상의 흐름에 따라 기술 또한 성숙하고 이에 따라 하나 둘씩 서비스가 생겨나면서 새로운 생태계가 탄생하였다. 물론 미쿡 이야기지만 우리나라에서도 카카오 플랫폼이 협력업체들과 폐쇄적으로 Open API 협업을 하긴 하는 듯하다. 하지만 생태계를 만들어 간다는 차원에서 그들만의 리그가 아닐까 싶다. 다른 나라에서 카카오의 플랫폼을 활용하려고 몇달씩 대기자 명단을 받으며 기다리고 함께 만들어 가고 싶어할까? 아마도 그것은 개발자 사이트와 Open API 정책 결여 그리고 기타등등 여러 사정이 있겠지만 역시 카카오 같은 모습은 앞으로 지향해 가야할 스타트업 회사들의 과제가 아닐까 싶다. 


    * 퇴근길에 "소프트웨어 개발자에게 성공이란" 글을 읽었다. 카카오에 대해 느낀점 그리고 비즈니스에 대해서 느낀점, 기획한 제품을 끌고 가기위해 그동안 했던 행동들이 주마등처럼 스쳐지나간다. 나의 목표는 https://www.facebook.com/groups/smartdashboard  에서 계속 글로 표현해 가고 있다.



    <참조>

      - 당신의 API가 RESTful 하지 않은 이유

    posted by 윤영식
    2013. 3. 16. 16:56 NodeJS/Modules

    AMD를  사용하지 않으면 엔터프라즈급의 웹앱을 만들 수가 없다. 요즘 Java 에서 Spring Framework이 엔터프라이즈 구현에 핵심이듯이 서로의 의존관계를 관리하고 주입하여 준다. 전통적인 방식은 HTML에서 <script> 태그를 사용하여 모듈들을 순차적으로 코딩한다. 그런 순차적 코딩이 아닌 모듈개념으로 어떻게 전환할 수 있는지 알아보자 



    1) 예전 방식의 자바스크립트 로딩방식

      - html 코드

        + add.js : 더하기 연산

        + multi.js : 곱하기 연산

        + app.js : add, multi 연산을 사용하고 결과 출력

    <html>

    <body>

    <script type="text/javascript" src="add.js"></script>

    <script type="text/javascript" src="multi.js"></script>

    <script type="text/javascript" src="app.js"></script>

    </body>

    </html>


      - 나머지 코드들

        + 전역변수 cal를 사용하기 위하여 모든 .js에서 undefined을 확인해야 한다

    // add.js 

    var cal = cal || {};


    cal.add = function(x, y) {

    return x + y;

    }


    // multi.js

    var cal = cal || {};


    cal.multiply = function(x, y) {

    return x*y;

    }


    // app.js 

    // 전역변수 cal를 이용하여 호출한다 

    console.log('add result is ' + cal.add(4, 5));

    console.log('multi result is ' + cal.multiply(4, 5));


      - html 호출 결과 

      



    2) AMD 방식으로 로딩하기

      - add.js, multi.js  모듈화를 위하여 define() 펑션을 이용한다

      - app.js 에서 모듈을 사용하기 위하여 require() 펑션을 이용한다

    // add.js 

    // var cal 과 같은 변수 선언이 없어졌다. 

    // 1) Functional Programming의 최대 장점이 변수 설정으로 인한 메모리 할당이 필요없어지게 되었다

    // 2) Anonymous Function == Closure Function으로 만들었다  

    define(function() {

    return function(x, y) {

    return x + y;

    }

    }) 


    // multi.js

    define(function() {

    return function(x, y) {

    return x * y;

    }

    })


    // app.js

    // 의존관계에 있는 것을 require의 첫번째 인자에 배열로 설정한다 

    require(['add', 'multi'], function(add2, multi2) {

    console.log('AMD : add result is ' + add2(4, 5));

    console.log('AMD : multi result is ' + multi2(4, 5));

    });


      - AMD 구현체 require.js를 사용하여 index.html 을 변경한다 

        + data-main 값으로 app.js 를 지정한다

        + src 값으로 require.js 를 지정한다 

    <html>

    <BODY>

    <script data-main="app" src="http://requirejs.org/docs/release/2.1.5/minified/require.js"></script>

    </BODY>

    </html>


      - 결과 확인

      


    ** 테스트 소스

    AMD.zip



    <참조>

      - Require.js 홈페이지

      - Require.js 이용하여 AMD 모듈 작성하기

    posted by 윤영식
    2013. 3. 16. 12:34 NodeJS/Modules

    Node.js에서 모듈을 사용하는 방법과 브라우져에서 SPA(Singe page application) 기반으로 개발을 진행할 때 Javascript 모듈의 의존성 관계 관리를 위한 모듈 사용방법은 차이가 있다. 각각에 대해 간단히 알아보자 


    1) Node.js 모듈 

      - CommonJS 패턴을 사용한다 : Synchronous 로딩이다 

      - require('dowon') 방식으로 module을 로딩한다 

      - module.exports 를 통하여 functionality를 노출시킨다. 모듈이라는 컴포넌트를 사용하기 위한 외부 Interface를 지정하는 것이다.


      - calc.js 모듈 파일 

    var Calc = function(start) {
    	var that = this;
    	
    	this.add = function(x) {
    		start = start +x;
    		return that;
    	};
    
    	this.multiply = function(x) {
    		start = start * x;
    		return that;
    	};
    
    	this.equals = function(callback) {
    		callback(start);
    		return that;
    	};
    }
    // module.exports를 사용한다 
    module.exports = {
    	add: function(x, y) {
    		return new Calc(x).add(y || 0);
    	},
    	multiply: function(x, y) {
    		return new Calc(x).multiply(y || 1);	
    	}
    }
    


    - app.js 모듈 파일 

    // require를 호출하여 모듈 즉, 컴포넌트를 로딩한다
    var Calc = require('./calc.js');
    
    // 모듈에서 외부로 노출시킨 API를 호출한다
    Calc.add(1, 2)
    	.multiply(3)
    	.equals(function (result) {
    		console.log(result);
    	});
    



    2) 브라우져 기반 모듈

      - Require.js 를 -AMD(Asynchronous Module Definition)- 사용한다 : Asynchronous 로딩이다

      - CommonJS와 약간의 차이가 있다

      - require 를 통해 dependencies와 callback을 얻는다 : 주로 호출하는 Main 입장의 application에서 사용할 때 

      - define 을 통해 dependencies를 얻고 API를 노출한다 : 개별 모듈을 정의할 때  

      - RequireJS사용하기 블로그

      - 제품 리스트를 보여주는 메인 소스 

    //require 사용
    require(['jquery', './big-cart'], function($, bigCart) {  <=== big-cart.js 의 펑션을 호출한다. 즉, big-car.js에 의존관계
    	$(document).ready(function() {
    		bigCart.init();
    	});
    })
    


      - 쇼핑 카트 

    // define 사용하여 정의
    define(['./pubsub', 'jquery'], function(pubsub, $) {  <=== pubsub.js 의 펑션을 호출한다. 즉, pubsub.js에 의존관계
    	var cart, count = 0;
    
    	pubsub.sub('add-to-cart', function(itme) {
    		count++;
    
    		cart.find('h1').html(count);
    
    		var li = $('< li >')
    			.html(item.name)
    			.data('key', item.id);
    
    		cart.find('ul').append(li);
    
    	});
    
    	pubsub.sub('remove-from-cart', function(item) {
    		count--;
    
    		cart.find('h1').html(count);
    		cart.find('li').filter(function() {
    			return $(this).data('key') == item.id;
    		}).remove();
    
    	});
    
    	return {
    		init: function() {
    			cart = $('.big-cart');
    		}
    	}
    });
    


      - 카트 내역 관리 모델 

    define(function() {  <=== 의존하는 것이 없다 
    	var cache = {};
    	return {
    		pub: function(id) {
    			var args = [].slice.call(arguments, 1);
    			if(!cache[id]) {
    				cache[id] = [];
    			}
    
    			for(var i=0, il=cache[i].length; i -1) {
    					cache[id] = cache[id].slice(0, index).concat(cache[id].slice(index+1));
    				}
    			}
    		}
    	}
    });
    



    3) CommonJS와 Require.js 개념 정리


    4) AMD 개념


    5) AMD 동영상
      - 하나의 자바스크립트 파일의 내역을 재사용할 수 있도록 - DRY (Don't Repeatly Yourself) - 모듈로 분리함
        + 첫째는 단순히 <script> 태그를 사용하여 개발자가 순서대로 넣는 호랑이 담배피던 시절의 방식
        + 둘째는 define을 사용하여 모듈라이제이션하고 사용시에 require 한다 
      - part01
     


    posted by 윤영식
    2013. 3. 14. 11:48 NodeJS/Concept

    그동안 혼자 고민하여 찾아 해메였던 많은 부분이 채수원씨의 발표자료안에 잘 정리되어 있고, 결과물을 만들며 얻은 팁을 공유하고 있다. 발표 자료를 통해 느낀점을 적어본다 (채수원씨는 2011년 KOSTA에서 MongoDB 수강하며 알게 됨. 강사였음 ^^)



    1) Devnote 사용하는 모듈들 보며 느낀점 

      - 미경험 모듈 : zombie.js, node-i18n, jake, winston, gitfs 등

      - 경험 모듈 : 대부분 노드에서 가장 많이들 사용하는 모듈로 결정

      - 테스트 모듈 : step 코드의 간결함이 좋음

      - 개발툴로 CodeMirror 사용해 봐야겠다. 현재는 Sublime Text 2 를 사용중 임

      - 서버쪽 템플릿은 Jade, SPA(Single Page Application) 개발로 Backbone.js를 사용한다면 Underscore.js를 사용하자 

      



    2) Devnote 아키텍쳐보며 느낀점

      - 스토리지를 gitfs로 했는데 해당 부분을 mongodb로 바꾸고 싶은 욕구가 생김

      - 테스트 자동화에서 브라우저 애뮬레이션으로 CasperJS를 추가하고 싶음  

      - view 영역에서 Backbone.js 가 들어갔으면 함

      - 전부 자바스크립트로 개발 및 테스트 환경을 꾸몄다. 물론 소스도 자바스크립트이다. 

        이제 JavaScript 도 Java처럼 개발과 테스트 환경 생태계가 엄청나게 빨리 성숙해 지고 있다. 이젠 대세~~~ 정대세 ^^;

      - CoffeeScript를 본격적으로 사용하자. 기본 문법수준에서 알고 조금씩 했지만 이젠 가급적 커피로 가자고 결심함 ^^;

        코드의 가독성과 컴파일된 .js 파일의 자체 모듈패턴 적용이 좋음 

      - i18n 을 고려해야 겠다고 생각함 

      - git 스타일의 파일 관리면 버전관리가 가능하겠구나 생각함. 위키니깐~~

      - socket-io 를 통하여 real-time 피드백을 받는다는 개념이 좋음. 역시 피드백은 바로 받아 바로 처리해야~~

      - Markdown 은 배우기도 쉽다. 그리고 소스 문서화 툴 Groc 에서도 사용하니 필히 익혀서 쓰자 

      - 우리 솔루션에도 Twitter Bootstrap CSS 스타일을 따라야 겠다고 생각함. 눈에 익숙한 것이 거부감을 줄여줄 것이라 생각함



    3) 발표자료

      - 시월의 맑은 하루 (OctoberSky.js) 페북모임에 관심이 감 

      - source map 설정을 통해서 자바스크립트 디버깅 가독성있게 하기를 테스트 해봐야 겠음 

      - 자바스크립트 개발자라면 Front-end <-> Back-end 개발 다 하자. 

        페이스북이 그렇게 개발한다. 그리고 테스트팀도 없다. 개발자들이 기본 TDD를 한다 

      



    <참조>

      - 채수원씨 발표자료

      - DevNote : https://github.com/nforge/devnote

      - CodeMirror : https://github.com/marijnh/CodeMirror

      - Markdown 포멧 설명문법설명, GitHub GFM

    posted by 윤영식
    2013. 3. 11. 11:51 NodeJS/Concept

    Node.js 에서 가장 많이 사용하는 Express 프레임워크를 다시 둘러보면서 Connect를 살펴보았다. 데이터 스트림속에서 특정 객체를 가르키는 기술을 쿼리라고 본다. 예로 jQuery는 DOM내에서 특정 노드 객체를 쉽게 찾을 수 있고, Backbone은 el를 통하여 객체를 Fragment화해서 특정 위치에 Render해 준다



    1. 자바스크립트 쿼리의 시대

      - 제일 쿼리 : Underscore.js 

        + 클라이언트 사이드의 Backbone.js의 핵심이 된다


      - 제이 쿼리 : jQuery 

        + 두말이 필요없다. 브라우져 호환성과 셀렉터 그리고 다양한 위젯과 스마트 디바이스 지원까지 


      - 제삼 쿼리 : Backbone.js 

        + Client MV* Framework

        + 클라이언트 SPA (Single Page Web Application) 개발시 기본이 되는 프레임워크 


      - 삼.오 쿼리 : Node.js 

        + 서버 사이드 엔진 (Async I/O, One Thread)

        + Modern Web App 개발


      - 제사 쿼리 : Express & Connect 

        + Server MV* Framework

        + Node.js가 Java의 JVM 이라면 Connect 는 Java의 WAS(Web Application Server) or Apache 기능을 갖춘 middleware 이다

        + Express는 Connect 모듈을 통해 RESTful Web Services를 쉽게 만들게 해주는 Java의 Spring Framework 이다 


      - 제오 쿼리 : MongoDB

        + NoSQL

        + 최신버전의 REPL은 V8 engine (Node.js와 동일한 구글의 JavaScript 엔진 사용예정)

        + HA를 위한 쉬운 Replica Set 지원

        + Scale-out을 위한 Replica Set단위의 Sharding 지원



    2. Connect 살펴보기

      - Node.js에서 모자란 부분을 확장하여 미들웨어 개념으로 제공을 한다.

        미들웨어는 단지 펑션일 뿐이고 사용자가 얼마든지 확장하여 사용할 수 있다.

        Connect가 HTTP 서비스에 대하여 많은 부분 추상화를 하였고, Express가 Connect를 또 Wrapping 하고 있다

      - 출처 :  http://www.senchalabs.org/connect/

      - logger

        + 아파치의 format string을 생각하면 된다 

        + 아파치처럼 %d 와 같은 옵션이 아니라 :date 같은 옵션이다

        + 아파치처럼 다양한 옵션을 제공하진 않는다. %b 와 같이 전송 바이트수 옵션이 없다


      - csrf

        + Cross-site request 로 인한 위조 방지 

        + _csrf 필드가 hidden으로 추가됨 (req.session._csrf, req.body._csrf, req.query._csrf, req.headers['x-csrf-token'])


      - compress

        + gzip 압축지원


      - basicAuth

        + callback(user, pass) 를 지원하여 true를 리턴하면 접근 허용


      - bodyParser

        + request body 파싱 및 appplication/json, application/x-www-form-urlencoded, multipart/form-data 파싱 

        + Express 에서 사용 : express.bodyParser()


      - json

        + JSON 요청 파싱하여 req.body에 파싱한 오브젝트 제공


      - urlencoded

        + x-www-form-urlencoded 요청 파싱하여 req.body에 파싱한 오브젝트 제공


      - multipart

        + multipart/form-data 요청 파싱하여 req.bodyd와 req.files 에 파싱한 오브젝트 제공

        + connect.multipart({uploadDir: path}) 경로를 설정한다 


      - timeout

        + 요청에 대한 timeout (ms 단위), 기본 5000 (ms, 즉 5초)

        + err.status=408 이면 next()를 통하여 timeout 응답페이지 처리함 


      - cookieParser

        + 헤더 쿠키를 파싱하여 req.cookies 객체 제공

        +  req.secret  스트링 전달 가능 


      - session

        + session() 사용하려면 cookieParser()를 반드시 사용해야 한다 (세션아이디 sid 저장)

        + req.session 으로 접근 

        + SessionStore는 MemoryStore 이고 별도 구현하려면 규정된 몇가지 메소드를 구현하면 됨 

           세션 클러스터링에 대한 구현이 필요할 수 있겠다 


      - cookieSession

        + 쿠키 사용 


      - methodOverride

        + req.originalMethod 로 form의  method(GET, POST 같은)가 넘어가는 것을 _method로 override

     

      - responseTime

        + X-Response-Time 헤더 표시 (milliseconds)


      - staticCache

        + 정적 파일 서비스를 위한 메모리 캐싱. 기본 128 개의 오브젝트 캐싱

        + 각 256k 사이즈로 32mb 까지 캐싱가능 

        + Least-Recently-Used (LRU) 알고리즘 사용 : 사용빈도 높은 것이 캐싱

        + respondFormCache() 통하여 캐싱 응답 status=304


      - static 

        + 루트 경로 지정

        + express.static(__dirname + '/public') 형태로 사용함 


      - directory

        + 주어진 루트 경로의 목록

        + . (dot) 파일은 숨김


      - vhost

        + VirtualHost통한 sub domain 설정


      - favicon

        + 설정하지 않으면 /public/favicon.ico 찾음 


      - limit 

        + request body 크기 제한  

        + 5mb, 200kb, 1gb 등으로 설정


      - query

        + query-string을 자동으로 파싱

        + req.query 오브젝트 생성


      - errorHandler

        + error 처리 핸들러에게 stack traces 제공

        + text or json형태로 에러 메세지 응답 

        + /public/error.html 참조



    3. 테스트하기 

      - GitHub connect Test Doc

      - GitHub connect Test Source

      - 테스트는 mocha에 should를 적용하여 BDD 수행한다. (참조)

    $ git clone https://github.com/senchalabs/connect.git

    Cloning into 'connect'...

    remote: Counting objects: 15253, done.

    remote: Compressing objects: 100% (4656/4656), done.

    remote: Total 15253 (delta 10035), reused 14692 (delta 9538)

    Receiving objects: 100% (15253/15253), 3.10


    $ cd connect 

    $ npm install .

    $ mocha --reporter list 

    ... 중략 ...

        at Server.app (/Users/nulpulum/git-repositories/connect/lib/connect.js:65:37)

        at Server.EventEmitter.emit (events.js:99:17)

        at HTTPParser.parser.onIncoming (http.js:1928:12)

        at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:111:23)

        at Socket.socket.ondata (http.js:1825:22)

        at TCP.onread (net.js:404:27)

      ․ connect.urlencoded() should accept a limit option: 2ms

      ․ connect.urlencoded() should support all http methods: 1ms

      ․ connect.urlencoded() should parse x-www-form-urlencoded: 1ms

      ․ utils.uid(len) should generate a uid of the given length: 1ms

      ․ utils.parseCacheControl(str) should parse Cache-Control: 0ms

      ․ utils.mime(req) should return the mime-type from Content-Type: 0ms

      ․ connect.vhost() should route by Host: 2ms

      ․ connect.vhost() should support http.Servers: 1ms

      ․ connect.vhost() should support wildcards: 1ms

      ․ connect.vhost() should 404 unless matched: 2ms

      ․ connect.vhost() should treat dot as a dot: 1ms


      192 tests complete (2 seconds)


      - mocha 수행하면 열라 에러 많이 뜬다 ㅠ.ㅠ; 멘붕온다. 해당 메세지가 정상적인 것인지 아닌지 살펴보자

    // mocha.opts 내역
    --require should
    --require test/support/http

     --growl


      - connect의 Code Coverage 보기 



    <참고>

      - Express 가이드 한글화 (필독)

      - Connect의 다양한 Samples in GitHub

      - 지난주 부터 수강하고 있는 KOSTA 이복영강사님 강의에서는 제사쿼리를 MongoDB 라고 지칭하였고, 제* 쿼리를 도용하여 내 나름으로 Express & Connect를 하나 더 넣었다

      - Connect 개념 설명 및 간단 예제

      - JavaScript Code Coverage 프레임워크


    posted by 윤영식
    2013. 2. 16. 11:21 NodeJS/Concept

    웹 기술에 대한 발전방향을 고찰해 보자.


    1) Physical Level

      - file base - static web (web server : content management, http 통신) - web browser (mime type)

        + Content Delivery Service

      - GET, POST 만 주로 사용



    2) Logical Level

      - framework operation (MVC라는 server side기술에서 출발함)

      - controller : request/response, model : data, view : ui  

      - DRY Service: Don't Repeat Yourself 를 위한 좋은 framework으로 요즘 spring 을 기업에서 많이 사용함 

      - Routing 통한 RESTful Web Services : Data Service -> Cloud Service 로 발전 (CRUD 서비스를 GET/POST/PUT/DELETE)

      - RIA 기술의 태동 : 

      - MVC 초기 document base 에서 stream base 로 이동한다. 이에 따라 DB도 code first 로 이동하면서 어플리케이션과 merge를 쉽게 하는 방향으로 이동한다 

      - Data Service로 가면서 View가 없어지면서(None UI) SPA(MS의 서버사이드 기술임)을 스티브 샌더슨이 말하고 있다

      - 즉,  RIA (activex, flash, etc) -> SPA 로 변해감 (기술이 JavaScript로 통일되어 감)

      - application level 이다 



    3) SNS Level

      - SNS 기술 : community level, Service Level 이다 

      - 2)의 application level 과 service level 로 구분된다 

      - Client-WebApp, Server-Node.js, NoSQL-MongoDB 기술들의 등장 

      - Client Side에서 SPA (Single Page Application)등장 : Backbone(Underscore), Angular (DI 존재), Ember (DI 없음)

       + 2)레벨의 MVC는 의미가 희석됨

       + Backbone의 $el 는 3세대 jQuery 라고 보면됨 

       + Functional programming이 가능 

       + Backbone -> AngularJS로 이동하면 된다 

      - Client + Server + Store 를 합치는 기술 = Meteor 또는 Derby 

      - MV* = Functional 이라는 개념으로 이동한다. 이것은 뷰가 모델이 되고, 컨트롤러가 없어지고, 나뉘었다 합쳐졌다하면서 데이터만 남는다. 즉, 과거 MVC 처럼 역할이 나뉘지 않는다

       


    4) Contextual Level

      - 융합 서비스

      - Trend (500만) -> Culture (1000만) 즉, SNS를 통하여 트랜드나 문화로 가는 서비스 레벨

      - 데이터에 대한 MapReduce만 남는다 

        + Map = Key + Value 예) JSON

        + Reduce Function = Business Intelligence 솔루션으로 가는 것이다


    결론, 기술 변천을 느끼고 만들어 가려면 Coding 하자.... ^^


    <참조>

      - KOSTA : 이복영 강사, MongoDB/Node.js 강의 4주차 

    posted by 윤영식
    2013. 1. 31. 16:34 NodeJS/Concept

    Node.js 에서 Express를 사용하면서 일관된 축약 언어 컴파일러로 JavaScript 코딩은 CoffeeScript를 사용하고, HTML은 Jade를 사용하고 CSS는 Stylus를 사용할 때 가장 기본적인 뼈대가 되는 코드를 만들어 주는 프레임워크 Cham을 알아보자. 


    1) Cham 설치하기 

      - Node.js 와 NPM, CoffeeScript는 기본 설치되어 있어야 한다 

      - git clone 으로 설치하기 

    $ git clone https://github.com/conancat/cham.git

    Cloning into 'cham'...

    remote: Counting objects: 155, done.

    remote: Compressing objects: 100% (103/103), done.

    remote: Total 155 (delta 48), reused 142 (delta 35)

    Receiving objects: 100% (155/155), 136.89 KiB | 59 KiB/s, done.

    Resolving deltas: 100% (48/48), done.


      - Express, jade, stylus 설치하기 

      - 설치후 브라우져 호출 : http://localhost:3000/ 

    //////////////////////////////////////

    // 설치 

    $ cd cham

    $ npm install express

    npm http GET https://registry.npmjs.org/express/2.4.3

    npm http 200 https://registry.npmjs.org/express/2.4.3

    ... 중략 ...

    npm http 200 https://registry.npmjs.org/formidable

    express@2.4.3 node_modules\express

    ├── mime@1.2.9

    ├── qs@0.5.3

    └── connect@1.9.2 (formidable@1.0.11)


    $ npm install jade

    npm http GET https://registry.npmjs.org/jade

    ... 중략 ...

    npm http 200 https://registry.npmjs.org/commander/-/commander-0.6.1.tgz

    jade@0.28.1 node_modules\jade

    ├── commander@0.6.1

    ├── mkdirp@0.3.4

    └── coffee-script@1.4.0


    $ npm install stylus -g

    npm http GET https://registry.npmjs.org/stylus

    ... 중략 ...

    C:\Users\yuwonsystem01\AppData\Roaming\npm\stylus -> C:\Users\yuwonsystem01\AppD

    ata\Roaming\npm\node_modules\stylus\bin\stylus

    stylus@0.32.0 C:\Users\yuwonsystem01\AppData\Roaming\npm\node_modules\stylus

    ├── debug@0.7.0

    ├── mkdirp@0.3.4

    └── cssom@0.2.5


    $ npm install vows

    npm http GET https://registry.npmjs.org/vows

    npm http 200 https://registry.npmjs.org/vows

    npm http GET https://registry.npmjs.org/vows/-/vows-0.7.0.tgz

    ... 중략 ...

    npm http 200 https://registry.npmjs.org/diff/-/diff-1.0.4.tgz

    vows@0.7.0 node_modules\vows

    ├── eyes@0.1.8

    └── diff@1.0.4


    ///////////////////////////////////

    // 실행하기 

    $ node app

    Express server listening on port 3000 in development mode



    2) 구조파악하기 

      - root 폴더에 CakeFile이 존재하여 cake를 수행할 수 있다. (CoffeeScript 형태로 작성)

    /////////////////////////////////////

    // cake를 실행하면 사용법이 출력됨 

    D:\git-nulpulum\cham>cake

    Cakefile defines the following tasks:


    cake watch             # Watches all Coffeescript(JS) and Stylus(CSS) files

    cake watchJS         # Watches all coffeescript files for changes

    cake watchCSS      # Watches all CSS files for changes

    cake compileJS       # Compiles all Coffeescript files into JS

    cake test                # Runs all tests

    cake docs              # Create documentation using Docco


    //////////////////////////////////////////////

    // watch 하고 있는 원본 소스들을 볼 수 있다

    // 원본 소스는 전부 src에 위치함

    D:\git-nulpulum\cham>cake watch

    15:10:11 - compiled src\lib\helpers.coffee

    15:10:11 - compiled src\lib\module.coffee

    15:10:11 - compiled src\lib\conf.coffee

    15:10:11 - compiled src\test\testHelpers.coffee

    15:10:11 - compiled src\public\js\plugins.coffee

    15:10:11 - compiled src\app.coffee

    15:10:11 - compiled src\test\example.test.coffee

    15:10:11 - compiled src\lib\routes.coffee

    15:10:11 - compiled src\public\js\script.coffee

      watching C:\Users\yuwonsystem01\AppData\Roaming\npm\node_modules\stylus\lib\functions\index.styl

      watching src\public\css\conf\base.styl

      watching src\public\css\conf\helpers.styl

      watching src\public\css\conf\colors.styl

      watching src\public\css\elems\reset.styl

      watching src\public\css\elems\helpers.styl

      watching src\public\css\elems\typography.styl

      watching src\public\css\elems\common.styl

      watching src\public\css\pages\layout.styl

      watching src\public\css\pages\index.styl

      watching src\public\css\media\media.styl

      compiled public\css\style.css

      watching src\public\css\style.styl

    15:15:16 - compiled src\app.coffee       <== app.coffee 를 열어서 살짝 수정하였더니 자동으로 재컴파일 됨 


      - 의존관계 파악을 위해 package.json 파일을 열어보자 

    {

      "name": "cham",

      "version": "0.0.1",

      "private": true,

      "dependencies": {

        "express": "2.4.3",

        "jade": ">= 0.0.1"

      },

      "devDependencies": {

        "vows": ">= 0.5.9",     // BDD 테스트

        "stylus": ">= 0.13.9",  // CSS

        "coffee-script": ">= 1.1.2" // Javascript 

      },

      "engines": {

        "node": "*"

      },

      "author": "Grey Ang <conancat@gmail.com> (http://grey-ang.com)",

      "description": "Boilerplate for quick Coffeescript driven app, backed by Jade and Stylus",

      "repository": {

        "type": "git",

        "url": "git://github.com/conancat/cham.git"

      },

      "scripts": {

        "test": "cake test"

      }

    }


      - CoffeeScript 문법의 CakeFile : "task <명칭>, <설명>, -> 펑션"  task 다음에 3개의 아규먼트로 cake task 만듦

        + watchJS 명령 : coffee -cw -o ./ src/

        + watchCSS 명령 : stylus --watch --include ./public --out ./public/css ./src/public/css

        + compileJS 명령 : coffee -c -o ./ src/

        + test 명령 : vows test/*.test.js

        + docs 명령

           docco src/*.coffee

           docco src/lib/*.coffee

           docco src/test/*.coffee 

    //////////////////////////////////////////
    // CakeFile 내역 
    # Module requires
    {spawn, exec} = require 'child_process'
    sys = require 'sys'
    
    # ## Helpers
    
    # Helper function for showing error messages if anyting happens
    printOutput = (process) ->
      process.stdout.on 'data', (data) -> sys.print data
      process.stderr.on 'data', (data) -> sys.print data
      
    # Watch Javascript for changes
    watchJS = ->
      coffee = exec 'coffee -cw -o ./ src/'
      printOutput(coffee)
    
    # Watch CSS for changes
    watchCSS = ->
      
      # Without Nib
      stylus = exec 'stylus --watch --include ./public --out ./public/css ./src/public/css'
      
      # Use this line instead if you're using Nib
      # stylus = exec 'stylus --watch --include ./public --use ./node_modules/nib/lib/nib.js --out ./public/css ./src/public/css'
      
      printOutput(stylus)
      
    # ## Tasks
    # I guess pretty self explainory? lol
    task 'watch', 'Watches all Coffeescript(JS) and Stylus(CSS) files', ->
      watchJS()
      watchCSS()
    
    task 'watchJS', 'Watches all coffeescript files for changes', ->
      watchJS()
      
    task 'watchCSS', 'Watches all CSS files for changes', ->
      watchCSS()
      
    task 'compileJS', 'Compiles all Coffeescript files into JS', ->
     coffee = exec "coffee -c -o ./ src/"
     printOutput(coffee)
      
    task 'test', 'Runs all tests', ->
      vows = exec 'vows test/*.test.js'
      printOutput(vows)
      
    task 'docs', 'Create documentation using Docco', ->
      docco = exec """
        docco src/*.coffee
        docco src/lib/*.coffee
        docco src/test/*.coffee
      """
      printOutput(docco)
    



    3) 사용한 모듈들 알아보기

      - Stylus

        + CSS를 축약해서 표현, 사용이 간단하고, 코드가 간결해짐 

        + ; 과 { } 를 사용할 필요가 없고  DRY (Don't Repeat Yourself) 지원


      - Docco

        + 주석을 MarkDown 으로 줄 수 있다 (GitHub README.md 만들기와 일관성 있어 좋군)

        + 수행한 위치 바로 밑에 docs 폴더에 위치함 

        + 설치 : npm install -g docco

        + 수행 : docco src/*.coffee


      - Jade

        + HTML 템플릿 엔진 : 이것도 < > 사용이 필요없어 코드가 간결해짐

        + 사용 설명


      - Vows

        + 비동기 BDD for Node

    //////////////////////////////////////////
    // vowtest.js
    var vows = require('vows'),
        assert = require('assert');
    
    // Create a Test Suite
    vows.describe('Division by Zero').addBatch({
        'when dividing a number by zero': {
            topic: function () { return 42 / 0 },
    
            'we get Infinity': function (topic) {
                assert.equal (topic, Infinity);
            }
        },
        'but when dividing zero by zero': {
            topic: function () { return 0 / 0 },
    
            'we get a value which': {
                'is not a number': function (topic) {
                    assert.isNaN (topic);
                },
                'is not equal to itself': function (topic) {
                    assert.notEqual (topic, topic);
                }
            }
        }
    }).run(); // Run it 

    ///////////////////////////////////////

    // 결과 : 성공 

    D:\git-nulpulum\cham>node vowtest.js

    ··· ✓ OK » 3 honored (0.036s)



      - Express

        + 설치 : npm install -g express

        + 수행 : $ express <projectName>

    //////////////////////////////////////////
    // Express로 Listen Server 구성
    var express = require('express');
    var app = express();
    
    app.get('/', function(req, res){
      res.send('Hello World');
    });
    
    app.listen(3000);
    


      - CoffeeScript

        + 설치 : npm install -g coffee-script

        + 실행 : coffee <파일명>.coffee    <== .coffee 확장자 생략가능   



    <참조> 

      - https://github.com/conancat/cham

    'NodeJS > Concept' 카테고리의 다른 글

    [Node.js] Express의 Connect 살펴보기  (0) 2013.03.11
    [Node.js] 기술 발전 방향 4단계  (0) 2013.02.16
    [Node.js] 생태계에 대한 생각들  (0) 2012.12.23
    [Jade] Jade 사용하기  (0) 2012.12.15
    [EJS] 사용하기  (0) 2012.12.15
    posted by 윤영식
    2013. 1. 30. 16:03 NodeJS/Prototyping

    한개의 Socket.io 인스턴스를 공유하여 3개의 독립적인 채팅 서버를 운영하는 방법과 Express.js 사용시 virtual host를 설정하여 sub domain을 만드는 방법을 알아본다.



    1. 가정

      - mydomain.com / sub1.mydomain.com / sub2.mydomain.com 의 서로 독립적인 채팅서버 운영

      - socket.io 인스턴스는 하나만 사용하여 공유함

      - 테스트 사이트 구경하기



    2. GitHub에서 소스 다운로드

      - git 기본 설치 되었음을 가정한다 (참조)

      - 각 분리된 namespace에서 Chat 애플리케이션 3개의 인스턴스를 수행할 것이다

      - 소스 설치하기 

    [mongodb@localhost ~]$ git clone git://github.com/braitsch/sub.socket.io.git socket-io-example

    Initialized empty Git repository in /home/mongodb/socket-io-example/.git/

    ... 중략 ..

    Resolving deltas: 100% (56/56), done.

    [mongodb@localhost ~]$ cd socket-io-example/

    [mongodb@localhost socket-io-example]$ pwd

    /home/mongodb/socket-io-example


    ///////////////////////////////////////////

    // 각 분리된 namespace 존재 

    [mongodb@localhost socket-io-example]$ ls 

    README.md  

    mydomain.com 

    sub1.mydomain.com  

    sub2.mydomain.com



    3. mydomain.com의 app.js 와 mydomain.com/subdomain/sub1(2).js 소스이해

      - Express 사용하기 

    // app.js

    var exp = require('express');

    var app = exp.createServer();


      - socket.io Listen 설정 : 전역(gobal)으로 Socket.IO 인스턴스에 접근하도록 선언한다 

    // app.js

    // create the single instance of socket.io that will be shared across all applications //

    // 한개의 socket.io인스턴스를 생성하여 모든 애플리케이션간 공유한다

    global.socket = require('socket.io').listen(app);

    global.socket.set('log level', 1);

    global.socket.set('transports', [ 'websocket', 'flashsocket', 'htmlfile', 'xhr-polling', 'jsonp-polling']);


      - subdomain 만들기

        + virtual host로써 동작하는 subdomain 애플리케이션을 설정한다

    // create subdomain applications //

    app.use(exp.vhost('sub1.' + global.host, require('./subdomains/sub1')));

    app.use(exp.vhost('sub2.' + global.host, require('./subdomains/sub2')));


      - 모듈 로딩하면서 수행

    // finally create this application, our root server //

    // chat-socket.js 파일 모듈 로딩

    require('./app/config')(app, exp);

    require('./app/server/router')(app);

    require('./app/server/modules/chat-socket');


      - sub1(2).js 소스 : 채팅 커넥션과 메세지를 다루기위해 chat-socket을 포함한 Express 애플리케이션이다

    var exp = require('express');

    var app = exp.createServer();


    app.root = global.root + '/sub1.mydomain.com';

    require(app.root + '/app/config')(app, exp);

    require(app.root + '/app/server/router')(app);

    require(app.root + '/app/server/modules/chat-socket');

    module.exports = app;



    4. chat-socket.js 소스이해

      - socket.io에 네임스페이스를 '/chat'으로 정의하기 

    // pwd : ./socket-io-example/mydomain.com/app/server/modules

    module.exports = function()

    {

    // 랜덤하게 사용자 색깔 지정 

    var colors = ['#AE331F', '#D68434', '#116A9F', '#360B95', '#5F209E'];

            // 사용자 커넥션 추적을 유지하기 위한 객체 생성 

    var connections = { };

    // if you use socket.of(), you can set namespace. 

            // chat 네임스페이스 정의하고 connect  되면 수행할 펑션들 정의 

    global.socket.of('/chat').on('connection', function(socket) {

           // give each connected user a random color so it's easier to tell them apart in the chat log //

                   // 채팅사용자가 준비상태 

    socket.on('user-ready', function(data) {

    socket.name = data.name;

    socket.color = data.color = colors[Math.floor(Math.random() * colors.length)];

    brodcastMessage('user-ready', data);

    });


                   // 메세지 전송하기 

    socket.on('user-message', function(data) {

    data.color = socket.color;

    brodcastMessage('user-message', data);

    });


                    // 접속자 상태 전송 

    function dispatchStatus()

    {

    brodcastMessage('status', connections);

    }

                    // 메세지 전송하기 

    function brodcastMessage(message, data)

    {

                   // remove socket.emit if you don't want the sender to receive their own message //

    socket.emit(message, data);

    socket.broadcast.emit(message, data);

    }

                    // soket.io 커넥션 끊길 때 호출 

            // handle connections & disconnections //

    connections[socket.id] = {}; dispatchStatus();

    socket.on('disconnect', function() {

    delete connections[socket.id]; dispatchStatus();

    brodcastMessage('user-disconnected', { name : socket.name, color : socket.color });

    });

    });

    }();


      - sub1.mydomain.com 에 대하여 네임스페이스를 틀리게 기술한다 

    global.socket.of('/chat-sub1').on('connection', function(socket) ...


      - sub2.mydomain.com 에 대하여 네임스페이스를 틀리게 기술한다 

    global.socket.of('/chat-sub2').on('connection', function(socket) ...



    5. index.js와 index.jade 소스이해

      - index.jade 하단에 index.js 와 socket.io.js 포함 

    div.navbar.navbar-fixed-top

    div.navbar-inner

    div.container-fluid

    ul.nav.pull-left

    .brand Node.js & Socket.IO Chat Server

    ul.nav.pull-right

    li

    a(href='#')#connected


    #container

    #send-message.well.span4

    h2 Say Something

    hr

    label Your Name 

    input(type="text", name="name", id='name').span4

    label Your Message

    textarea(rows='6', name="msg", id='msg').span4

    button#btn-send.btn.btn-primary

    i.icon-pencil.icon-white

    | Send it !

    #messages.well.span4

    h2 Conversation

    hr

    #incoming


    script(src='/vendor/jquery.min.js')

    script(src='/socket.io/socket.io.js')

    script(src='/js/index.js')


      - index.js 파일

    $(document).ready(function() {


    $('#msg').focus();

            // give user a generic name to start //

            // 사용자가 접속하면 랜덤 명칭을 설정한다 

    $('#name').val(Math.random().toFixed(8).toString().substr(2));

    $('#btn-send').click(function(){ sendMessage(); })

    $('#msg').keypress(function(e){ if (e.keyCode === 13) { sendMessage(); return false; } })


           // initialize the socket connection to listen on the 'chat' namespace //

    socket = io.connect('/chat');

    socket.on('status', function (connections) {

    var i=0; for (p in connections) i++;

    var s = i > 1 ? ' are '+i+' People ' : ' is '+i+' Person ';

    $('#connected').html('There '+s+' Currently Connected');

    });

    socket.on('user-ready', function (data) {

    $('#incoming').append('<span class="shadow" style="color:'+data.color+'">'+data.name +' :: connected</span><br>');

    autoScroll();

    });

    socket.on('user-message', function (data) {

    $('#incoming').append('<span class="shadow" style="color:'+data.color+'">'+data.name +' :: '+ data.message+'</span><br>');

    autoScroll();

    });

    socket.on('user-disconnected', function (data) {

    $('#incoming').append('<span class="shadow" style="color:'+data.color+'">'+data.name +' :: disconnected</span><br>');

    autoScroll();

    });


            // register the user's name with the socket connection on the server // 

    socket.emit('user-ready', {name : $('#name').val() });

    var autoScroll = function() { 

    document.getElementById('incoming').scrollTop = document.getElementById('incoming').scrollHeight; 

    }


            // 메세지 보내기 

    var sendMessage = function() {

    socket.emit('user-message', {name : $('#name').val() , message : $('#msg').val() });

    $('#msg').val('')

    }

    });



    7. 채팅 서비스 설정하기 

      - 채팅 서비스에 대한 수행전에 필요한 모듈에 대해서 install 한다 

    ///////////////////////////////////////////////////////////////

    // mydomain.com 에 stylus, jade, express, socket.io 설치

    // package.json 참조하여 설치함

    $ cd mydomain.com 

    $ npm install -d 

    ... 중략 ...

    stylus@0.32.0 node_modules/stylus

    jade@0.28.1 node_modules/jade

    express@2.5.8 node_modules/express

    socket.io@0.9.13 node_modules/socket.io

    npm info ok 


    // mydomain.com 안에 모듈 설치 디렉토리 생성

    [mongodb@localhost mydomain.com]$ ll
    total 20
    drwxrwxr-x. 4 mongodb mongodb 4096 Feb  1 23:17 app
    -rw-rw-r--. 1 mongodb mongodb 1109 Feb  1 23:17 app.js
    drwxrwxr-x. 7 mongodb mongodb 4096 Feb  2 19:49 node_modules
    -rw-rw-r--. 1 mongodb mongodb  590 Feb  1 23:17 package.json
    drwxrwxr-x. 2 mongodb mongodb 4096 Feb  1 23:17 subdomains

    //////////////////////////////////////////////////////
    // sub1/sub2 도메인에 jade, stylus, express 설치 
    // (socket.io는 mydomain에만 설치됨)
    $ cd ../sub1.mydomain.com
    $ npm install -d 
    ... 중략 ...
    npm info prepublish sub.socket.io@1.0.0
    jade@0.28.1 node_modules/jade
    stylus@0.32.0 node_modules/stylus
    express@2.5.8 node_modules/express


      -  /etc/hosts 파일에 다음을 설정한다 : 브라우져에서 호출하기 위한 sub1, sub2 도메인을 설정한다 

    127.0.0.1 sub1.localhost

    127.0.0.1 sub2.localhost



    8. Chat Node Server 실행하기

      - mydomain.com 디렉토리로 들어가서 Node를 실행한다

    /home/mongodb/socket-io-example/mydomain.com

    [mongodb@localhost mydomain.com]$ node app

       info  - socket.io started

    Express server listening on port 8080 in development mode 


      - 브라우져 2개를 띄워서 http://localhost:8080 을 동일하게 실행한다 

        + dowon 채팅창과 다른 브라우져창에서 try 명칭의 사용자가 들어왔다

        + socket.io를 통하여 채팅을 주고 받고 있다


      - 브라우져 2개를 띄워서 http://sub1.localhost:8080 을 동일하게 실행한다 (http://sub2.localhost:8080 동일 수행함)

        + youngsik 채팅창과 다른 브라우져창에서 girl 명칭의 사용자가 들어왔다

        + mydomain.com의 동일한 socket.io를 통하여 채팅을 주고 받고 있다 (namespace만 틀릴 뿐이다)



    <참조>

      - 원문 : Building a Node.js Chat Application and Sharing Socket.IO Across Multiple Subdomains

      - 소스 : https://github.com/braitsch/sub.socket.io

      - Express.js Virtual Host 설정

      - 원문과 동일한 socket.io 인스턴스 한개와 Virtual Host 를 통한 SubDomain 공유에 대한 동일 예제


    posted by 윤영식
    2013. 1. 29. 21:57 NodeJS/Prototyping

    Node.js를 설치하고 MongoDB를 사용하여 REST API를 만들어보자. native MongoDB driver를 사용하며 REST API를 쉽게 만들기 위하여 Node.js의 웹애플리케이션 프레임워크인 Express를 이용한다. 원문의 내용을 보고 수행한 결과를 요약한다 



    1) Node.js 설치 

      - http://nodejs.org/dist/  : 사이트에서 최신 버전을 선택하여 다운로드한다 

      - 예) linux 

    [mongodb@localhost ~]$ wget http://nodejs.org/dist/v0.9.8/node-v0.9.8-linux-x86.tar.gz 

    --2013-02-01 21:24:48--  http://nodejs.org/dist/v0.9.8/node-v0.9.8-linux-x86.tar.gz

    Resolving nodejs.org... 165.225.133.150

    Connecting to nodejs.org|165.225.133.150|:80... connected.

    HTTP request sent, awaiting response... 200 OK

    Length: 4491882 (4.3M) [application/octet-stream]

    Saving to: “node-v0.9.8-linux-x86.tar.gz”


    100%[=========================================================>] 4,491,882    284K/s   in 17s     


    2013-02-01 21:25:07 (263 KB/s) - “node-v0.9.8-linux-x86.tar.gz” saved [4491882/4491882]


    [mongodb@localhost ~]$ ls

    aggregation  mongo  mongod  mongodb  mongofiles  mongos  node-v0.9.8-linux-x86.tar.gz  shard

    [mongodb@localhost ~]$ tar zxf node-v0.9.8-linux-x86.tar.gz

    [mongodb@localhost ~]$ ls

    aggregation  mongod   mongofiles  node-v0.9.8-linux-x86         shard

    mongo        mongodb  mongos      node-v0.9.8-linux-x86.tar.gz

    [mongodb@localhost ~]$ rm node-v0.9.8-linux-x86.tar.gz

    [mongodb@localhost ~]$ mv node-v0.9.8-linux-x86 node-v0.9.8

    [mongodb@localhost ~]$ ls

    aggregation  mongo  mongod  mongodb  mongofiles  mongos  node-v0.9.8  shard

    [mongodb@localhost ~]$ cd node-v0.9.8/

    [mongodb@localhost node-v0.9.8]$ cd bin

    [mongodb@localhost bin]$ ./node -v

    v0.9.8


    2) Node.js 수행하기 

      - 적당한 디렉토리에 nodecellar 디렉토리 만듦

      - nodecellar 안에 server.js 파일 생성

      - server.js 코딩 

    ////////////////////////////////////////////////
    // server.js
    var http = require('http');
    http.createServer(function (req, res) {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end('Hello World\n');
    }).listen(3000, '127.0.0.1');
    console.log('Server running at http://127.0.0.1:3000/');
    


      - server.js 수행하고 브라우져에서 http://127.0.0.1:3000/ 호출 

    [mongodb@localhost ~]$ cd nodecellar/

    [mongodb@localhost nodecellar]$ node server.js

    Server running at http://127.0.0.1:3000/



    3) Express 설치하기 

      - Node.js의 Web Application Framework 이고, REST API 생성을 쉽게 해준다 

      - nodecellar 디렉토리안에 package.json 파일을 만들어서 Node.js가 의존하는 모듈을 정의한다 

    ////////////////////////////////////////////
    // package.json
    {
        "name": "wine-cellar",
        "description": "Wine Cellar Application",
        "version": "0.0.1",
        "private": true,
        "dependencies": {
            "express": "3.x"
        }
    }
    

      - express 설치하기 

    // package.json의 의존관계 모듈을 읽어서 자동 설치해 준다 

    [mongodb@localhost nodecellar]$ npm install

    npm WARN package.json wine-cellar@0.0.1 No README.md file found!

    npm http GET https://registry.npmjs.org/express

    npm http 200 https://registry.npmjs.org/express

    npm http GET https://registry.npmjs.org/express/-/express-3.1.0.tgz

    ... 중략 ...
    express@3.1.0 node_modules/express
    ├── methods@0.0.1
    ├── fresh@0.1.0
    ├── range-parser@0.0.4
    ├── cookie-signature@0.0.1
    ├── buffer-crc32@0.1.1
    ├── cookie@0.0.5
    ├── commander@0.6.1
    ├── debug@0.7.0
    ├── mkdirp@0.3.3
    ├── send@0.1.0 (mime@1.2.6)
    └── connect@2.7.2 (pause@0.0.1, bytes@0.1.0, formidable@1.0.11, qs@0.5.1)



    4) server.js를 Express 형식으로 바꾸기 

      - express 프레임워크를 이용하여 HTTP Listener를 바꾼다

    ////////////////////////////////////////////
    // server.js
    var express = require('express');
     var app = express();
     
    app.get('/wines', function(req, res) {
        res.send([{name:'wine1'}, {name:'wine2'}]);
    });
    app.get('/wines/:id', function(req, res) {
        res.send({id:req.params.id, name: "The Name", description: "description"});
    });
     
    app.listen(3000);
    console.log('Express Listening on port 3000...');
    

      - express 실행

    [mongodb@localhost nodecellar]$ node server

    Express Listening on port 3000...


      - 브라우져 호출하기 

        + 전체 wine들 

      

        + 1번 wine 내역 

      


    5) Node.js에서 Controller로써 routes를 만들기 

      - nodecellar 디렉토리 밑으로 routes 폴더를 만든다 

      - routes 밑에 모듈로 wines.js 를 만든다 

    ////////////////////////////////////////////
    // routes 디렉토리 밑의 wines.js
    exports.findAll = function(req, res) {
        res.send([{name:'wine1'}, {name:'wine2'}, {name:'wine3'}]);
    };
     
    exports.findById = function(req, res) {
        res.send({id:req.params.id, name: "The Name", description: "description"});
    };
    


      - server.js 파일에서 wines.js 모듈을 로딩하게 한다. 그리고 다시 브라우져로 호출한다 

    ////////////////////////////////////////////
    // server.js에서 wines.js 모듈 로딩
    var express = require('express'),
        wines = require('./routes/wines');
    var app = express();
     
    app.get('/wines', wines.findAll);
    app.get('/wines/:id', wines.findById);
     
    app.listen(3000);
    console.log('Express Listening on port 3000...');
    


    6) MongoDB Driver 설치하기 

      - MongoDB는 설치되어 있다고 가정한다 (참조)

      - 여기서는 native Node.js driver를 사용한다

      - mongodb driver 설치하기 

    [mongodb@localhost nodecellar]$ npm install mongodb

    npm WARN package.json wine-cellar@0.0.1 No README.md file found!

    npm http GET https://registry.npmjs.org/mongodb

    ...  중략 ...

    npm http GET https://registry.npmjs.org/bson/-/bson-0.1.6.tgz

    ... 중략 ...

    node-gyp clean

    node-gyp configure build

    gyp http GET http://nodejs.org/dist/v0.9.8/node-v0.9.8.tar.gz

    gyp http 200 http://nodejs.org/dist/v0.9.8/node-v0.9.8.tar.gz

    make[1]: Entering directory `/home/mongodb/nodecellar/node_modules/mongodb/node_modules/bson/build'

      CXX(target) Release/obj.target/bson/ext/bson.o

      SOLINK_MODULE(target) Release/obj.target/bson.node

      SOLINK_MODULE(target) Release/obj.target/bson.node: Finished

      COPY Release/bson.node

    make[1]: Leaving directory `/home/mongodb/nodecellar/node_modules/mongodb/node_modules/bson/build'

    child process exited with code 0

    mongodb@1.2.11 node_modules/mongodb

    └── bson@0.1.6



    7) REST API 정의하기 

      - 호출 내역 

        + GET: 전체 wine 목록

        + GET: id로 선택된 wine 정보얻기 

        + POST: 새로운 wine 생성

        + PUT: id로 선택된 wine 정보갱신

        + DELETE: id로 선택된 wine 삭제하기 

      

      

      - server.js에서 wines.js를 확장한 메소드를 추가한다 

      - wines.js 안에 MongoDB 코드로 변경하기 



    8) MongoDB 와 Node.js 수행하고 Testing 하기 

      - MongoDB는 default port로 수행한다 

    [mongodb@localhost ~]$ ./mongod --dbpath /home/mongodb/aggregation

    ... 중략 ...

    Fri Feb  1 22:30:46 [initandlisten] options: { dbpath: "/home/mongodb/aggregation" }

    Fri Feb  1 22:30:46 [initandlisten] Unable to check for journal files due to: boost::filesystem::basic_directory_iterator constructor: No such file or directory: "/home/mongodb/aggregation/journal"

    Fri Feb  1 22:30:46 [initandlisten] couldn't unlink socket file /tmp/mongodb-27017.sockerrno:1 Operation not permitted skipping

    Fri Feb  1 22:30:46 [initandlisten] waiting for connections on port 27017

    Fri Feb  1 22:30:46 [websvr] admin web console waiting for connections on port 28017



      - Node.js 수행하기 

         + MongoDB의 기본 port 27017 로 접근한다 

    [mongodb@localhost nodecellar]$ node server

    ========================================================================================

    =  Please ensure that you set the default write concern for the database by setting    =

    =   one of the options                                                                 =

    =                                                                                      =

    =     w: (value of > -1 or the string 'majority'), where < 1 means                     =

    =        no write acknowlegement                                                       =

    =     journal: true/false, wait for flush to journal before acknowlegement             =

    =     fsync: true/false, wait for flush to file system before acknowlegement           =

    =                                                                                      =

    =  For backward compatibility safe is still supported and                              =

    =   allows values of [true | false | {j:true} | {w:n, wtimeout:n} | {fsync:true}]      =

    =   the default value is false which means the driver receives does not                =

    =   return the information of the success/error of the insert/update/remove            =

    =                                                                                      =

    =   ex: new Db(new Server('localhost', 27017), {safe:false})                           =

    =                                                                                      =

    =   http://www.mongodb.org/display/DOCS/getLastError+Command                           =

    =                                                                                      =

    =  The default of no acknowlegement will change in the very near future                =

    =                                                                                      =

    =  This message will disappear when the default safe is set on the driver Db           =

    ========================================================================================

    Express Listening on port 3000...

    Connected to 'winedb' database


      - curl을 수행한다 

    /////////////////////////////////////////

    // POST를 통하여 신규 wine을 생성한다

    [mongodb@localhost ~]$ curl -i -X POST -H 'Content-Type: application/json' -d '{"name": "New Wine", "year": "2009"}' http://localhost:3000/wines

    HTTP/1.1 200 OK

    X-Powered-By: Express

    Content-Type: application/json; charset=utf-8

    Content-Length: 79

    Date: Sat, 02 Feb 2013 06:38:49 GMT

    Connection: keep-alive


    {

      "name": "New Wine",

      "year": "2009",

      "_id": "510cb4798859a9b51d000001"


    /////////////////////////////////////////

    // MongoDb 확인 

    [mongodb@localhost ~]$ mongo

    MongoDB shell version: 2.2.2

    connecting to: test

    > show dbs

    dowonDB 0.03125GB

    local (empty)

    winedb 0.0625GB

    > use winedb

    switched to db winedb

    > show collections

    system.indexes

    wines

    > db.wines.find()

    { "name" : "New Wine", "year" : "2009", "_id" : ObjectId("510cb4798859a9b51d000001") }


    /////////////////////////////////////////

    // GET 명령 수행하여 정보 조회

    // wine 목록을 얻어온다.

    [mongodb@localhost ~]$ curl -i -X GET http://localhost:3000/wines

    HTTP/1.1 200 OK

    X-Powered-By: Express

    Content-Type: application/json; charset=utf-8

    Content-Length: 93

    Date: Sat, 02 Feb 2013 06:41:37 GMT

    Connection: keep-alive


    [

      {

        "name": "New Wine",

        "year": "2009",

        "_id": "510cb4798859a9b51d000001"

      }

    ]


    // 특정 wine 조회. _id를 넣어준다 

    [mongodb@localhost ~]$ curl -i -X GET http://localhost:3000/wines/510cb4798859a9b51d000001

    HTTP/1.1 200 OK

    X-Powered-By: Express

    Content-Type: application/json; charset=utf-8

    Content-Length: 79

    Date: Sat, 02 Feb 2013 06:42:14 GMT

    Connection: keep-alive


    {

      "name": "New Wine",

      "year": "2009",

      "_id": "510cb4798859a9b51d000001"



    /////////////////////////////////////////

    // PUT 명령 수행하여 정보 수정 

    [mongodb@localhost ~]$ curl -i -X PUT -H 'Content-Type: application/json' -d '{"name": "New Wine", "year": "2010"}' http://localhost:3000/wines/510cb4798859a9b51d000001
    HTTP/1.1 200 OK
    X-Powered-By: Express
    Content-Type: application/json; charset=utf-8
    Content-Length: 42
    Date: Sat, 02 Feb 2013 06:44:00 GMT
    Connection: keep-alive

    {
      "name": "New Wine",
      "year": "2010"


    /////////////////////////////////////////

    // DELETE 명령 수행하여 정보 삭제

    [mongodb@localhost ~]$ curl -i -X DELETE http://localhost:3000/wines/510cb4798859a9b51d000001
    HTTP/1.1 200 OK
    X-Powered-By: Express
    Content-Type: application/json; charset=utf-8
    Content-Length: 2
    Date: Sat, 02 Feb 2013 06:46:22 GMT
    Connection: keep-alive

    /////////////////////////////////////////

    // MongoDb 확인 

    [mongodb@localhost ~]$ mongo
    MongoDB shell version: 2.2.2
    connecting to: test
    > use winedb
    switched to db winedb
    > show collections
    system.indexes
    wines
    > db.wines.find()    <=== 데이터가 삭제되어 없음 



    <참조>

      - 원문 : Creating a REST API using Node.js, Express, and MongoDB

      - 참조 

        + https://speakerdeck.com/jakobmattsson/writing-restful-web-services-using-nodejs

        + Node.js 설치하기

        + MongoDB 설치하기

      - 심화 : Backbone.js 와 Bootstrap, Node.js로 만들기 

    posted by 윤영식
    2013. 1. 24. 21:57 NodeJS

    Node.js 기반하에 Front-end 개발을 할 경우 텍스트 기반의 툴들을 알아보자.

      - Node.js로 개발할 때 commmand-line 유틸리티가 상당히 많고 이는 front-end 개발자에게 도움을 준다 

      - 다섯가지의 Node CLI 유틸리티를 알아보자 

      - Node와 npm이 설치되어 있음을 전제로 한다 



    1) UglifyJS

      - JavaScript를 압축하고 사이즈를 최소화해준다 (네트워크 전송량을 작게해서 성능향상을 기대할 수 있다) 

      - UglifyJS의 기능을 바로 웹상에서 사용하기   

    - 설치하기 

    D:\Development>npm install -g uglify-js

    npm http GET https://registry.npmjs.org/uglify-js


    - 명령어

    uglifyjs -b -o <output file> <input file>


    -b 옵션 : beautifies

    D:\Development>uglifyjs -b -o d:\development\if_uglify.js d:\development\if.js

    D:\Development>uglifyjs -o d:\development\if_uglify.js d:\development\if.js

    // coffee에서 컴파일된 javascript 소스 
    // Generated by CoffeeScript 1.4.0
    (function() {
      var x;
    
      x = 4;
    
      if ((0 <= x && x <= 10)) {
        console.log("true");
      }
    
    }).call(this);
    
    // -b 옵션을 주었을 경우 
    (function() {
        var x;
        x = 4;
        if (0 <= x && x <= 10) {
            console.log("true");
        }
    }).call(this);
    
    // -b 옵션을 주지 않았을 경우 
    (function(){var x;x=4;if(0<=x&&x<=10){console.log("true")}}).call(this);
    



    2) Grunt

      - JavaScript 프로젝트를 위한 task-based command line 빌드 툴

      - 미리 정의된 템플릿을 만들어 준다 : 테스트(QUnit & PhantomJS), Lint파일(JSHint), 최소화파일(UglifyJS), Static WebServer

      - 템플릿들은 Node Server에서 구동할 수 있다. (jQuery 템플릿도 가능)

      - 시작하기 가이드

      - Grunt 간단 소개

      - 좀 더 자세한 튜토리얼

    - 설치하기 : 상당히 많은 모듈을 함께 설치한다

    D:\Development>npm install -g grunt

    ... 중략 ...

    grunt

    ├── dateformat@1.0.2-1.2.3

    ├── colors@0.6.0-1

    ├── semver@1.0.14

    ├── async@0.1.22

    ├── hooker@0.2.3

    ├── underscore@1.2.4

    ├── underscore.string@2.1.1

    ├── uglify-js@1.3.4

    ├── nopt@1.0.10 (abbrev@1.0.4)

    ├── gzip-js@0.3.1 (crc32@0.2.2, deflate-js@0.2.2)

    ├── temporary@0.0.5 (package@1.0.1)

    ├── glob-whatev@0.1.8 (minimatch@0.2.9)

    ├── connect@2.4.6 (fresh@0.1.0, cookie@0.0.4, pause@0.0.1, bytes@0.1.0, crc@0.2.0, debug@0.7.0, formidable@1.0.11, qs@0.5.1, send@0.0.4)

    ├── prompt@0.1.12 (pkginfo@0.3.0, winston@0.5.11)

    ├── jshint@0.9.1 (minimatch@0.0.5, cli@0.4.3)

    └── nodeunit@0.7.4 (tap@0.4.0)


    - jquery 템플릿 만들어보기

    // 신규 디렉토리를 만듦

    D:\Development\grunt>mkdir jquery

    // 디렉토리 이동

    D:\Development\grunt>cd jquery

    // 명령 수행 

    D:\Development\grunt\jquery>grunt init:jquery

    Running "init:jquery" (init) task

    This task will create one or more files in the current directory, based on the environment and the answers to a few questions. Note that answering "?" to any question will show question-specific help and answering "none" to most question


    will leave its value blank.


    "jquery" template notes:

    Project name should not contain "jquery" or "js" and should be a unique ID not already in use at plugins.jquery.com. Project title should be a human-readable title, and doesn't need to contain the word "jQuery", although it may. For

    example, a plugin titled "Awesome jQuery Plugin" might have the name "awesome-plugin". For more information please see the documentation at https://github.com/jquery/plugins.jquery.com/blob/master/docs/manifest.md


    // 질문에 값을 입력한다 

    Please answer the following:

    [?] Project name jQueryTest

    [?] Project title (jQuerytest) DoWonQuery

    [?] Description (The best jQuery plugin ever.)

    [?] Version (0.1.0)

    [?] Project git repository (git://github.com/UserXP/jQueryTest.git)

    [?] Project homepage (https://github.com/UserXP/jQueryTest)

    [?] Project issues tracker (https://github.com/UserXP/jQueryTest/issues)

    [?] Licenses (MIT)

    [?] Author name (none)

    [?] Author email (none)

    [?] Author url (none)

    [?] Required jQuery version (*)

    [?] Do you need to make any changes to the above before continuing? (y/N)


    Writing CONTRIBUTING.md...OK

    Writing grunt.js...OK

    Writing libs/jquery/jquery.js...OK

    Writing libs/jquery-loader.js...OK

    Writing libs/qunit/qunit.css...OK

    Writing libs/qunit/qunit.js...OK

    Writing README.md...OK

    Writing src/jQueryTest.js...OK

    Writing test/jQueryTest.html...OK

    Writing test/jQueryTest_test.js...OK

    Writing LICENSE-MIT...OK


    Initialized from template "jquery".

    Done, without errors.

    // 필요한 라이브러리와 grunt.js 환경파일이 자동 생성된다 

    D:\Development\grunt\jquery>dir

    2013-01-25  오후 05:32    <DIR>          .

    2013-01-25  오후 05:32    <DIR>          ..

    2013-01-25  오후 05:32             2,091 CONTRIBUTING.md

    2013-01-25  오후 05:32             1,547 grunt.js

    2013-01-25  오후 05:32               551 jQueryTest.jquery.json

    2013-01-25  오후 05:32    <DIR>          libs

    2013-01-25  오후 05:32             1,066 LICENSE-MIT

    2013-01-25  오후 05:32               199 package.json

    2013-01-25  오후 05:32               604 README.md

    2013-01-25  오후 05:32    <DIR>          src

    2013-01-25  오후 05:32    <DIR>          test

                   6개 파일               6,058 바이트

                   5개 디렉터리  37,356,339,200 바이트 남음



    3) GruntIcon

      - css icon 만들기

      - PhantomJS가 필요하다 (phantomjs.exe 파일을 프로젝트 폴더에 놓는다)

      - grunt.js 파일에 GruntIcon 로드 설정을 한다

    - 설치

    D:\Development\grunt\jquery>npm install grunt-grunticon

    npm http GET https://registry.npmjs.org/grunt-grunticon

    ... 중략 ...

    grunt-grunticon@0.1.4 node_modules\grunt-grunticon

    └── grunt@0.3.17 (dateformat@1.0.2-1.2.3, colors@0.6.0-1, semver@1.0.14, asyn

    c@0.1.22, hooker@0.2.3, underscore@1.2.4, underscore.string@2.1.1, uglify-js@1.3

    .4, nopt@1.0.10, gzip-js@0.3.1, temporary@0.0.5, glob-whatev@0.1.8, connect@2.4.

    6, jshint@0.9.1, prompt@0.1.12, nodeunit@0.7.4)


    - grunt.js 파일 설정 넣기 

    /*global module:false*/

    module.exports = function(grunt) {


      // Project configuration.

      grunt.initConfig({

        pkg: '<json:jQueryTest.jquery.json>',

        meta: {

          banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +

            '<%= grunt.template.today("yyyy-mm-dd") %>\n' +

            '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +

            '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +

            ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */'

        },

        concat: {

          dist: {

            src: ['<banner:meta.banner>', '<file_strip_banner:src/<%= pkg.name %>.js>'],

            dest: 'dist/<%= pkg.name %>.js'

          }

        },

        min: {

          dist: {

            src: ['<banner:meta.banner>', '<config:concat.dist.dest>'],

            dest: 'dist/<%= pkg.name %>.min.js'

          }

        },

        qunit: {

          files: ['test/**/*.html']

        },

        lint: {

          files: ['grunt.js', 'src/**/*.js', 'test/**/*.js']

        },

        watch: {

          files: '<config:lint.files>',

          tasks: 'lint qunit'

        },

        jshint: {

          options: {

            curly: true,

            eqeqeq: true,

            immed: true,

            latedef: true,

            newcap: true,

            noarg: true,

            sub: true,

            undef: true,

            boss: true,

            eqnull: true,

            browser: true

          },

          globals: {

            jQuery: true

          }

        },

        uglify: {},

        grunticon: {

          src: "src/icons-source/",

          dest: "src/icons-output/"

        }

      });


      // Default task.

      grunt.registerTask('default', 'lint qunit concat min');

      grunt.loadNpmTasks('grunt-grunticon');

    };


    - grunticon 생성 (윈도우: grunt.cmd, 리눅스 : grunt )
    D:\Development\grunt\jquery>grunt.cmd grunticon
    Running "grunticon" task
    Look, it's a grunticon!

    grunticon now minifying the stylesheet loader source.
    grunticon loader file created.
    grunticon now spawning phantomjs...
    Done, without errors.

    // grunt.js에 환경설정한 icon-output이 생성된다 



    4) JSHint

      - JavaScript의 코드 품질 측정 툴 

      - 잠재적인 위험 요소를 찾아준다 : 웹기반 측정도 가능

    - 설치

    D:\Development\grunt\jquery>npm install -g jshint

    npm http GET https://registry.npmjs.org/jshint

    npm http 304 https://registry.npmjs.org/jshint

    npm http GET https://registry.npmjs.org/cli/0.4.3

    npm http GET https://registry.npmjs.org/minimatch

    npm http 304 https://registry.npmjs.org/cli/0.4.3

    npm http 304 https://registry.npmjs.org/minimatch

    npm http GET https://registry.npmjs.org/lru-cache

    npm http GET https://registry.npmjs.org/glob

    npm http 304 https://registry.npmjs.org/lru-cache

    npm http 304 https://registry.npmjs.org/glob

    npm http GET https://registry.npmjs.org/minimatch

    npm http GET https://registry.npmjs.org/graceful-fs

    npm http GET https://registry.npmjs.org/inherits

    npm http 304 https://registry.npmjs.org/graceful-fs

    npm http 304 https://registry.npmjs.org/inherits

    npm http 304 https://registry.npmjs.org/minimatch

    npm http GET https://registry.npmjs.org/lru-cache

    npm http GET https://registry.npmjs.org/sigmund

    npm http 304 https://registry.npmjs.org/sigmund

    npm http 304 https://registry.npmjs.org/lru-cache

    C:\Documents and Settings\UserXP\Application Data\npm\jshint -> C:\Documents and

     Settings\UserXP\Application Data\npm\node_modules\jshint\bin\hint

    jshint@0.9.1 C:\Documents and Settings\UserXP\Application Data\npm\node_modules\

    jshint

    ├── minimatch@0.0.5 (lru-cache@1.0.6)

    └── cli@0.4.3 (glob@3.1.17)

     

    - 수행 : jshint <file to check>

    D:\Development>jshint loop.js

    loop.js: line 15, col 2, Mixed spaces and tabs.


    1 error



    5) Node-static

      - 로컬 테스트시에 캐쉬/헤더 별도 셋팅가능

      - 로컬 웹서버로서 아무 디렉토리에서나 시작하면 ROOT 디렉토리가 된다

    - 설치

    D:\Development>npm install -g node-static

    npm http GET https://registry.npmjs.org/node-static

    npm http 200 https://registry.npmjs.org/node-static

    npm http GET https://registry.npmjs.org/node-static/-/node-static-0.6.7.tgz

    npm http 200 https://registry.npmjs.org/node-static/-/node-static-0.6.7.tgz

    npm http GET https://registry.npmjs.org/colors

    npm http GET https://registry.npmjs.org/optimist

    npm http 304 https://registry.npmjs.org/colors

    npm http 304 https://registry.npmjs.org/optimist

    npm http GET https://registry.npmjs.org/wordwrap

    npm http 304 https://registry.npmjs.org/wordwrap

    C:\Documents and Settings\UserXP\Application Data\npm\static -> C:\Documents and

     Settings\UserXP\Application Data\npm\node_modules\node-static\bin\cli.js

    node-static@0.6.7 C:\Documents and Settings\UserXP\Application Data\npm\node_mod

    ules\node-static

    ├── colors@0.6.0-1

    └── optimist@0.3.5 (wordwrap@0.0.2)


    - 수행 : 아무 디렉토리나 가서 수행 -> 브라우져에서 호출한다 

    D:\Development>static

    serving "." at http://127.0.0.1:8080


    - 또는 8080이 사용중이라면 옵션을 준다 

    static -p <port>  <directory> 


    * CLI를 이용하여 브라우져안에서 클라이언트단 유닛 테스트 도구 : bunyip


    <참조> 

    'NodeJS' 카테고리의 다른 글

    [Node.js] 라이언 일병 이야기 이해하기  (0) 2012.12.08
    [Node.js] 역사와 기초  (0) 2012.12.08
    [Node.js] 시작하기  (1) 2012.10.30
    posted by 윤영식
    2013. 1. 13. 12:42 NodeJS/Modules

    Socket.io안에서 사용되는 WebSocket 에 대한 프로토콜의 위치에 대하여 알아보자.


    1) WebSocket 등장배경

      - 처음엔 iframe을 활용

      - 그다음은 XMLHTTPRequest를 이용

      - HTML5에 와서 Websocket 등장

      - SPDY 포로토콜을 통한 전송 압축 (성능향상)

      - Server Send Events (SSE)에 대한 이해


    2) SPDY 프로토콜 등장배경

      - 실시간 Push 효과적 (압축 및 암호)

      - Node-SPDY 

      - Chrome/Firefox 지원


      - chrome://net-internals/#spdy  명령을 통해서 Chrome의 다양한 통신 내역을 살펴볼 수 있다.


    3) 결론
      - Chrome이나 Firefox에서만 구동 될 수 있는 솔루션을 만들경우
      - Real-time Push 서비스를 할 경우 성능을 높이기 위해 사용
      - Node.js를 사용할 경우도 지원 


    posted by 윤영식
    2012. 12. 23. 01:40 NodeJS/Concept

    Node.js를 리안달이 만들고 난후 프로그래밍의 패러다임이 바뀌고 있다. 스타트업을 위한 모바일 서비스 또는 솔루션의 기본 플랫폼으로 사용되고 있다. 특히 요즘은 Client + Server + DB 가 인터넷에서 하나로 연결되고 있다. 인터넷이 하나의 운영체제처럼 변해가고 있는 것이다. 


      - JavaScript : Client + Server + DB 프로그래밍을 할 수 있는 랭귀지로 자리 잡고 있다.

      - JSON : MongoDB를 사용하면 Client + Server + DB 를 하나의 데이터로 통일 할 수 있다.

      - Socket.io : 인터넷에 물려있는 모든 Tier의 통신을 HTTP 프로토콜을 통하여 Bi-Direction 양방향 통신을 할 수 있다.


    1) 인터넷에 연결된 모든 부분이 JSON을 통하여 데이터를 주고 받는다.


    2) Node.js 생태계에 나타난 Framework들 : Realtime쪽에는 전부 Socket.io 가 핵심 모듈로 사용되고 있다.  

      - Backbone에서 본 현재 현황



    3) Node.js 소개 + 코딩 + 클라우드 파운드리 


    4) Derby 와 Meteor 비교 


    5) Node에서도 TDD 개발은 중요하다. Node위에서 Server Side JavaScript 개발을 할 때 Vows와 Twitter를 이용한다.(24page)



    'NodeJS > Concept' 카테고리의 다른 글

    [Node.js] 기술 발전 방향 4단계  (0) 2013.02.16
    [Cham] Express, CoffeeScript, Jade, Stylus boilerplate 코드 생성하기  (0) 2013.01.31
    [Jade] Jade 사용하기  (0) 2012.12.15
    [EJS] 사용하기  (0) 2012.12.15
    [Node.js] Global Objects  (0) 2012.12.10
    posted by 윤영식
    2012. 12. 22. 17:42 NodeJS/Prototyping

    socket.io를 이용하여 간단한 chatting 애플리케이션을 만들어 보자 


    * 참조 : socket.io이용하여 chat 구현하기


    > Express를 통하여 chat 컨텍스트를 만든다 (참조)

    express chat


    > app.js 에 socket.io를 위한 /chat 네임스페이스를 설정하고(jade이용), socket.io에 대하여 설정한다

    // declaration

    var express = require('express');

    var app = express()

        , http = require('http').createServer(app)

        , io = require('socket.io').listen(http)

        , routes = require('./routes')

        , user = require('./routes/user')

        , chat = require('./routes/chat')

        , path = require('path');


    app.get('/chat', chat.list);


    // http listen

    http.listen(app.get('port'), function(){

        console.log("Express server listening on port " + app.get('port'));

    });


    // socket connection event on

    var chat = io

        .of('/chat')

        .on('connection', function(socket) {  // /chat uri로 접속이 들어오는 이벤트 처리 

            console.log('>>> chat server connection');


            socket.on('msg', function(data) { // msg 이벤트 처리 

                console.log('>>> data : ' + data.name + ', ' + data.msg);

                chat.emit('new', data); // new 이벤트를 발행하여 data를 다시 클라이언트들에게 전송 

            });

    })


    > jade 관련 파일 : chat.js 와 chat.jade 

    // /routes 디렉토리의 chat.js 파일

    exports.list = function(req, res){

        res.render('chat', { title: 'Chatting Example' });

    };


    //  /views 디렉토리의 chat.jade 파일

    doctype 5

    html

      head

        title= title

        link(rel='stylesheet', href='/stylesheets/style.css')

        script(src='/socket.io/socket.io.js')

        script(src='http://code.jquery.com/jquery-1.8.3.min.js')

        script(src='chat_module.js')

      body

        h1= title

        div#wrapper

            div#messages  // chat 메세지가 찍힌다 

            div.nic

                input#name(name='name', type='text')  // 이름 입력 창

            div

            textarea#message  // 메세지 입력 창

            br

            input#send(type='submit', value='Send')

        // chat_module.js에서 Chat 오브젝트를 초기화 한다 

        script

            $(document).ready(function() {

                Chat.initialize('/chat');

            });


    > chat_module.js 안에 로직을 담는다 : 해당 모듈은 express의 public 디렉토리 밑에 놓는다 (즉시 실행 함수로 만듦)

    (function() {

        window.Chat = {

          socket : null,


         // 초기화 시에 /chat uri 네임스페이스를 넣어주었다 

          initialize : function(socketURL) {

              console.log('step1 socketURL : %s', socketURL);

              this.socket = io.connect(socketURL);


              console.log('step2 conneciton ok : %s ', this.socket);


       // send 버튼을 누를 경우 실행 

              $('#send').click(function() {

                  Chat.send();

              });


        // textarea 메세지 입력하고 enter key 칠 경우 

              $('#message').keyup(function(evt) {

                 if((evt.keyCode || evt.which) == 13) {

                     Chat.send();

                     return false;

                 }

              });


              // 서버로 부터 new 이벤트가 오면 메세지를 추가하는 add 콜백 설정  

              this.socket.on('new', this.add);

          },


          // 서버로 부터 발행된 new 이벤트 처리하는 펑션

          add : function(data) {

              console.log('add message : %s', data.name +', '+ data.msg);

              var name = data.name || 'anonymous';

              var msg = $('<div class="msg"></div>')

                  .append('<span class="name">' + name + '</span>: ')

                  .append('<span class="text">' + data.msg + '</span>');

              console.log('add html : %s', msg);


              $('#messages').append(msg).animate({scrollTop: $('#messages').prop('scrollHeight')}, 0);

          },


          // 메세지 넣고 send 버튼을 클릭할 때 수행하는 펑션 

          send : function() {

              console.log('sending message : %s', $('#name').val() +', '+ $('#message').val());

              this.socket.emit('msg', {

                  name: $('#name').val(),

                  msg: $('#message').val()

              });


              $('#message').val('');

          }

        };

    }());



    > node app 명령으로 Node 서버를 실행시킨 후 브라우저로 호출한다 (http://localhost:3000/chat)

    youngsik과 dowon 끼리 대화하는 것을 서버가 전송하고 있다 


    * Express + Jade + Socket.io 프로젝트 파일 전체  

    chat.zip


    * js 파일이 변경될 때 마다 Node.js를 restart 하고 싶지 않다면 supervisor를 사용한다 

    posted by 윤영식
    2012. 12. 22. 14:46 NodeJS/Prototyping

    Node.js에서 Express.js를 사용하고 템플릿 엔진은 Jade를 사용한다. 이때 socket.io를 간단히 테스트 해보자 


    > 먼저 Socket.io 모듈를 설치한다 (물론 express 모듈도 사전 설치되었다 가정한다)

    npm install socket.io


    > Express.js를 통하여 만들고자 하는 컨텍스트를 만든다

    express day3


    > day3 디렉토리로 이동을 하면 app.js 파일이 있고 해당 파일안에 socket.io 모듈를 넣고 http 모듈과 체인닝을 해준다. 그리고 jade 설정이 되어 있는 socketio.js 모듈을 로딩한다 

    // app.js 파일 상단 코딩

    // 기존 코드 

    var express = require('express')

      , routes = require('./routes')  // index.js 파일 

      , user = require('./routes/user') // user.js 파일 

      , dowon = require('./routes/dowon')// dowon.js 파일 

      , http = require('http')

      , path = require('path');


    // 변경 코드 

    var express = require('express');

    var app = express()

      , http = require('http').createServer(app)   

      , io = require('socket.io').listen(http)  // http 모듈을 socket.io 모듈과 체인닝함 

      , routes = require('./routes')

      , user = require('./routes/user')

      , socketio = require('./routes/socketio') // socketio.js 파일 로딩 

      , path = require('path');


    > url rout 설정을 해줌 for jade

    // url route

    app.get('/', routes.index);

    app.get('/users', user.list);

    app.get('/socketio', socketio.io); //  브라우져에서 /socketio 호출하면 socketio 모듈의 io exports명칭의 펑션을 호출한다


    > socketio.jade 파일을 만들어서 views 디렉토리에 놓는다 

    // socketio.jade 파일 내역

    doctype 5

    html

      head

        title= title

        link(rel='stylesheet', href='/stylesheets/style.css')

        script(src='/socket.io/socket.io.js')

        script(src='http://code.jquery.com/jquery-1.8.3.min.js')

        script

            $(function() {

                var ws = io.connect();  // node에서 수행한 app.js의 socket.io listen으로 연결


                ws.emit('server_1');  // 서버쪽으로 server_1 이벤트 발행

                ws.on('client_1', function(msg) {  // 서버로부터 온 client_1 이벤트 처리 

                    $('#container').html(msg);

                });

            });

      body

        h1= title

        div#container


    > app.js 파일안에 socket.io 서버 코딩을 한다

    // socket on

    io.sockets.on('connection', function(socket) {

        console.log('server connection');


        socket.on('server_1', function(data) { // 클라이언트로 부터 온 server_1 이벤트 처리

            if(data != undefined) {

                console.log('>>>>>' + data);

            }


            var msg = 'hi youngsik';

            socket.emit('client_1', msg); // 클라이언트쪽으로 client_1 이벤트 발행

        });

    });


    > 브라우저에서 호출한다 그러면 잠시후 브라우져에서 "hi youngsik" 메세지가 div(id=container)에 뿌려진다


    * 테스트 전체 파일

    express_socketio.zip


    * Socket.io GitHub : 자세한 사용 설명 보기

    posted by 윤영식
    2012. 12. 15. 15:31 NodeJS/Concept

    Express 프레임워크에서 기본으로 사용하는 Jade에 대하여 알아보자. 



    ▶ app.js 연구 


    1) 우선 require를 사용하여 모듈패턴에 의한 모듈을 얻는다. 모듈로 얻음으로 namespace가 분리되므로 서로간의 충돌을 방지 할 수 있다. 물론 접근은 module.<exports Name> 으로 접근한다 

    var express = require('express')

      , routes = require('./routes')             // index.js 파일 

      , user = require('./routes/user')        // user.js 파일 

      , dowon = require('./routes/dowon') // dowon.js 파일 

      , http = require('http')

      , path = require('path');


    2) ./routes/dowon 으로 require 했으므로 routes 디렉토리 밑에 dowon.js 파일을 생성한다. hi 라는 펑션을 exports 객체에 설정하여 모든 인스턴스가 공유할 수 있게 한다. 물론 모듈안에서 공유가 된다. 단, function(req, res) {...} 로 구현하자. 

    exports.hi = function(req, res){  // dowon 모듈에서 hi 펑션 공유 

      res.render('dowon', { title: 'Hi DoWon', name: 'youngsik', age: '30' }); // dowon.jade 지정 

    };


    3) res.render('dowon', ...) 설정에서 dowon이라고 key를 넣었으므로 views 폴더 밑에 dowon.jade 파일을 생성한다. jade syntax에 맞추어 코딩을 하고 값은 #{key} 형식으로 코딩하면 json에서 값을 읽어 자동 셋팅하고, html 파일로 변환된다 

    - var dododo = function() { return 'dododo young!'; };


    p #{title}


    p hi dowon yun


    p

      | hi

      | dowon

      | yun


    p.

      hi

      dowon

      yun


    // error

    p

      hi

      dowon

      yun  


     ul

       li one

       li two

       li #{name}

       li #{age}

       li= dododo()


    // each loop

    //-

    - var users=[{ title: 'Hi DoWon1', name: 'youngsik1', age: '30' }, {title: 'Hi DoWon2', name: 'youngsik2', age: '40' }, { title: 'Hi DoWon3', name: 'youngsik3', age: '50' }];


     ul

         each user in users

            li= user.title

            li= user.name

            li= user.age



    4) app.js 에서 get 요청에 대하여 서비스 되는 모듈의 exports 펑션을 지정한다. 즉, /dowon uri가 호출되면 hi 펑션이 수행함

    app.get('/', routes.index);

    app.get('/users', user.list);

    app.get("/dowon", dowon.hi);


    Node의 require(<js 파일>) 호출을 통하여 모듈을 로딩하고, 모듈로 로딩된 js 파일안에 모듈에서 공유할 펑션을 정의하였다. 해당 펑션에는 jade 파일명을 지정하여 서비스 한다. 결국, Node의 require, exports Global객체를 적절히 사용하여 구성하였다. 하기와 같은 과정을 거치는 것이다



    Route → Route Handler → Template → HTML


    • Route : dowon.js
    • Route Handler : hi에 지정한 function
    • Template : dowon.jade
    • HTML : jade에 의하여 html로 변환 


    <참조>

      - ninja-express

      - Jade syntax 배우기 (필독)

      - Jade code : 동영상 (필감)


    posted by 윤영식
    prev 1 2 next