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

Publication

Category

Recent Post

2013. 3. 16. 16:17 Backbone.js

모듈 프로그래밍은 큰사이즈의 애플리케이션을 작은 단위의 관리가능한 블록으로 만드는데 유용하다. 모듈기반 코딩은 유지보수 노력을 줄여주고 재사용을 높여준다. 그러나 모듈간의 의존관계를 관리하는 부분은 어려운 점들이 있다. 이런 의존 관계상의 문제를 해결하기 위하여 RequireJS 프레임워크가 나오게 되었다. 



1) Old Style Loading JavaScript 파일들

  - 큰사이즈 애플리케이션들은 여러 자바스크립트 파일을 <script> 태그로 로딩한다

  - 이때 각 자바스크립트 파일은 어떻게 로딩을 처리하는지 예제로 보자


purchase.js

function purchaseProduct(){

  console.log("Function : purchaseProduct");

  var credits = getCredits();

  if(credits > 0){

    reserveProduct();

    return true;

  }

  return false;

}


products.js

function reserveProduct(){

  console.log("Function : reserveProduct");

  return true;

}


credits.js

function getCredits(){

  console.log("Function : getCredits");

  var credits = "100";

  return credits;

}


  - 만일 main.js에서 이렇게 코딩을 하면 var result = purchaseProduct(); 

  - purchase.js는 credits.jsproducts.js 파일 의존관계가 된다. 따라서 이들은 purchaseProduct()이 호출되지 전에 먼저 로딩되어 져야 한다. 

  - 만약 다음 순서라면 에러가 발생할 것이다. (credits.js 가 먼저 로딩되어야 하는데 맨 뒤에 있으므로)

<script src="products.js"></script>

<script src="purchase.js"></script>

<script src="main.js"></script>

<script src="credits.js"></script>


  

2) RequireJS 소개

  - http://requirejs.org/docs/download.html 에서 모듈을 다운로드하자 

  - 모든 자바스크립트 파일과 함께 require.js 파일을 같은 디렉토리에 둔다

    

  - index.html 안에 하기와 같이 script를 넣는다 

<script data-main="scripts/main" src="scripts/require.js"></script>


  - 코드안에 RequireJS를 사용하여 require code를 넣는다.

  - data-main 어트리뷰트 애플리케이션 최기화 시점으로 이때 RequireJS가 다른 스크립트 파일과 의존관계를 찾기위하여 scripts 디렉토리 밑에 있는 main.js를 사용하고 require.js를 src 값으로 지정한다. main.js가 해당 예에서는 같은 폴더에 모든 파일이 위치하지만 원하는 어느 폴더에 놓아도 무방하다. 


  - main.js 코드를 보자 

require(["purchase"],function(purchase){

  purchase.purchaseProduct();

});


  -  RequireJS는 require() 또는 define() 펑션으로 모든 코드를 감싼다. 펑션의 첫번째 파라미터로 의존관계에 있는 파일명을 넣으면 되고, .js는 생략가능하다. 두번째 파라미터는 무명함수(anonymous function)로 의존파일안의 함수를 호출한다. 

  - 다중의존관계 호출은 다음과 같다

require(["a","b","c"],function(a,b,c){

});



3) Define으로 모듈 생성하기

  - main.js 에서 사용될 모듈들을 define() 펑션을 이용하여 만든다

  - 모듈생성이 펑션널 프로그래밍 단위 즉 Task 단위로 생성이 되는 것이다. 

  - main.js 에서 필요한 모듈 즉 Task 를 로딩하여 사용하게 된다 

  - purchase.js를 바꾸어 보자

define(["credits","products"], function(credits,products) {

  console.log("Function : purchaseProduct");

  return {

    purchaseProduct: function() {

      var credit = credits.getCredits();

      if(credit > 0){

        products.reserveProduct();

        return true;

      }

      return false;

    }

  }

});


  - products.js 도 바꾸어 보자

define(function(products) {

  return {

    reserveProduct: function() {

      console.log("Function : reserveProduct");

      return true;

    }

  }

});


  - credits.js 도 바꾸어 보자 

define(function() {

  console.log("Function : getCredits");

  return {

    getCredits: function() {

      var credits = "100";

      return credits;

    }

  }

});



4) define()으로 모듈 만들고 require()로 필요한 모듈 사용하기 

  - require() : 모듈을 로딩하여 즉시 함수를 수행하는데 사용한다

  - define() : 모듈을 정의하는데 사용한다

  - purchaseProduct() 은 main.js 파일에서 함수를 즉시 실행한 것이다. 그러나 다른 파일은 재사용을 위하여 모듈화 하기 위하여 define()을 사용했다. 



5) 왜 RequireJS가 중요한가?

  - RequireJS는 펑션이 수행되기전에 모든 의존관계 파일이 로딩될 때까지 기다린다. 즉, 하나의 호출에 관여된 의존관계 파일을 로딩한 후에 펑션을 호출해 준다. 필요한 시점에 로딩하여 주고 Asynchronous Module Loading 이다. 

 


6) 의존관계 파일의 우선순위 관리 방법

  - RequireJS는 파일을 로딩할 때 Asynchronous Module Definition(AMD)을 사용한다.

  - Asynch로 로딩되기 때문에 순서적인 로딩을 보장받고 싶다면 shim 환경파일을 작성할 수 있다. 

  - 환경 shim 예

requirejs.config({

  shim: {

    'source1': ['dependency1','dependency2'],

    'source2': ['source1']

  }

});


  - RequireJS에서 제공하는 config() 펑션을 통해 shim 이라 불리우는 파라미터를 정의한다.

  - configuration 가이드는 http://requirejs.org/docs/api.html#config 를 참조한다. (Advanced User로 갈려면 꼭 익히자!)

    + jQuery config

    + Node config

    + Dojo config


  - 예로 하기와 같이 정의하지만 로딩 순서 지정을 위와 같이 shim으로 할 수 있는 것이다. 

define(["dependency1","dependency2","source1","source2"], function() {

);

  

  - source2는 source1을 의존한다. source1의 로딩이 끝나면 source2는 모든 의존파일이 로딩되었다 여긴다. 그러나 dependency1과 dependency2 는 여전히 로딩되고 있을지도 모른다. shim config를 사용하면 이러한 복잡성을 제거하여 로딩 순서를 지정하여 줄 수 있다. 


  - Backbone.js Boilerplate 블로깅의 index.html 파일을 파악해 보자



<참조>

  - 원문 : Understanding RequireJS for Effective JavaScript Module Loading

  - RequireJS 홈페이지

posted by 윤영식
2013. 3. 16. 16:00 Backbone.js

SPA를 작성하게 되면 클라이언트 사이드의 템플릿엔진을 사용하게 된다. 많은 템플릿 엔진이 나와 있고 이중 핸들바에 대해서 알아보자 



1) Handlebar.js 소개 

  - 클라이언트 사이드 템플릿 엔진 성능 비교에서 핸들바의 위치 

  - 핸들바의 동작방식 소개 동영상

  


2) 핸들바 템플릿 만들기

  - 핸들바 스크립트 최신 다운로드 받고 <script> 코드 넣기 (jQuery 기본으로 넣음) 

<html>

<body>

  <script src="jquery.js"></script>

  <script src="handlebar.js"></script>

</body>

</html>


  - 템플릿 코드 작성하기 

    + <script> 테그를 사용하면 화면에 렌더링되지 않음

    + handlebar.js 가 사용하는 type임을 x-handlebars-template 명칭으로 준다

    + 컬렉션의 루프틑 # 으로 시작해서 / 로 끝남 == Code Less

<script id="some-template" type="text/x-handlebars-template">

 <table>

   <thead>

     <th>Username</th>

     <th>Real Name</th>

     <th>Email</th>

   </thead>

   <tbody>

     {{#users}}

       <tr>

         <td>{{username}}</td>

         <td>{{firstName}} {{lastName}}</td>

         <td>{{email}}</td>

       </tr>

     {{/users}}

   </tbody>

 </table>

</script>


  - jQuery를 이용하여 템플릿 핸들링하기 

<script>

          // 템플릿 해석

 var source   = $("#some-template").html();

 var template = Handlebars.compile(source);

    // 데이터 

 var data = { users: [

     {username: "alan", firstName: "Alan", lastName: "Johnson", email: "alan@test.com" },

     {username: "allison", firstName: "Allison", lastName: "House", email: "allison@test.com" },

     {username: "ryan", firstName: "Ryan", lastName: "Carson", email: "ryan@test.com" }

   ]};

          // div id=content-placeholder 에 결과 내역을 렌더링한다 

 $("#content-placeholder").html(template(data));

</script>


  - template 변수가 참조하는 펑션 내용

function (context, options) {

    if (!compiled) {

      compiled = compile();

    }

    return compiled.call(this, context, options);

  } 


  - content-placeholder div 태그 넣기 (<script> 태그보다 위에 놓는다)

<html>

<body>

<div id="content-placeholder"></div>


  <script src="jquery.js"></script>

  <script src="handlebar.js"></script>

.. 중략 ..


  - 전체 소스 코드 

  - 결과 화면 

  



4) 클라이언트 사이드 템플릿 엔진

  - Mustache -> Handlebar -> JsRender 순서로 발전해 왔음 

  - JsRender : MS가 차기 Office 에 공식 채택한 템플릿 엔진



** 테스트 소스 

handlebar.zip


<참조>

  - 원문 : Getting Start With Handlebar.js


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. 15. 17:49 Backbone.js

서점 샘플 응용하기 - 01 에 이어서 02 에서는 서버-Node.js & MongoDB- 와 붙이는 작업을 한다. 원문이 과거 버전이어서 좀 더 이해하기 쉽도록 재구성하였고, RESTful API 테스트 방법도 새롭게 시도해 보았다



1) RESTful API 와 Node.js 환경 구성하기 

  - 호출하는 API 목록

/api/rides  GET 모든 라이딩정보의 배열을 얻는다.

/api/rides/:id  GET 아이디가 :id인 라이딩정보을 얻는다.

/api/rides  POST  새로운 라이딩정보를 추가하고 아이디가 추가된 그 정보를 반환한다.

/api/rides/:id  PUT 아이디가 :id인 라이딩정보를 수정한다.

/api/rides/:id  DELETE  아이디가 :id인 라이딩정보를 삭제한다.


  - node.js & mongodb 설치되어 있음을 가정하고 node.js 모듈을 설치한다

// 모듈 설치

$ npm install express

$ npm install mongoose


  - public 디렉토리를 생성하여 img, js, css, index.html 파일을 public 디렉토리 밑으로 옮긴다 

  


  - server.js 코딩하기

// 모듈 의존성

var express = require("express"); //웹 프레임워크


// 서버를 생성한다

var app = express();


// 서버를 설정한다

app.configure(function () {

    app.use(express.bodyParser()); //요청 바디를 파싱해서 req.body를 만든다

    app.use(express.methodOverride()); //HTTP 메쏘드를 덮어쓰기 위해서 req.body를 확인한다

    app.use(app.router); //url과 HTTP 메쏘드에 기반한 라우팅을 수행한다

    app.use(express.static(__dirname + '/public')); //정적 자원을 제공하는 곳

    app.use(express.errorHandler({ dumpExceptions:true, showStack:true })); //개발시점에 모든 에러를 보여준다

});


// 서버를 구동한다

app.listen(8080, function () {

    console.log("Express server listening on port %d in %s mode", 8080, app.settings.env);

});


  - nodemon 을 통해서 구동하기 : 수정된 코딩이 있으면 자동으로 restart node 수행 (nodemon 블로깅)

nodemon server.js

15 Mar 14:37:11 - [nodemon] v0.7.2

15 Mar 14:37:11 - [nodemon] watching: ~/backbone/riding

15 Mar 14:37:11 - [nodemon] starting `node server.js`

Express server listening on port 8080 in development mode


  - MongoDB 구동하기 

// mongod 데몬 실행 

$ mongod --dbpath /mongodb/ride_database/

Fri Mar 15 15:33:19 [initandlisten] MongoDB starting : pid=50361 port=27017 dbpath=/Users/nulpulum/mongodb/ride_database/ 64-bit host=mac-42-retina.local

Fri Mar 15 15:33:19 [initandlisten] db version v2.2.3, pdfile version 4.5

... 중략 ..

Fri Mar 15 15:33:19 [websvr] admin web console waiting for connections on port 28017

Fri Mar 15 15:33:19 [initandlisten] waiting for connections on port 27017

// Node.js 서버가 구동 되어 있으면 연결 정보가 출력된다 

Fri Mar 15 15:34:27 [initandlisten] connection accepted from 127.0.0.1:61129 #1 (1 connection now open)

Fri Mar 15 15:34:27 [initandlisten] connection accepted from 127.0.0.1:61130 #2 (2 connections now open)

Fri Mar 15 15:34:27 [initandlisten] connection accepted from 127.0.0.1:61131 #3 (3 connections now open)

Fri Mar 15 15:34:27 [initandlisten] connection accepted from 127.0.0.1:61132 #4 (4 connections now open)



2) Mongoose 통하여 MongoDB 접근하기 

  - biz.js 파일을 생성하고 mongoose 코딩하기

    + Mongoose 용 schema와 model을 만든다 

    + MongoDB 에 접속한다 

    + 모듈패턴으로 업무 callback CRUD function 들을 GET, POST, PUT, DELTE 메소드를 정의한다

  - server.js 안에 RESTful API 추가하기

.. 중략 ..

// 서버를 설정한다

app.configure(function () {

    app.use(express.bodyParser()); //요청 바디를 파싱해서 req.body를 만든다

    app.use(express.methodOverride()); //HTTP 메쏘드를 덮어쓰기 위해서 req.body를 확인한다

    app.use(app.router); //url과 HTTP 메쏘드에 기반한 라우팅을 수행한다

    app.use(express.static(__dirname + '/public')); //정적 자원을 제공하는 곳

    app.use(express.errorHandler({ dumpExceptions:true, showStack:true })); //개발시점에 모든 에러를 보여준다

});


// mongoose 업무모듈인 biz.js 가져오기 

var biz = require("./biz");


// RESTful API에 대한 호출에 대하여 biz.js 모듈의 callback 펑션을 설정함

app.get( '/api/rides', biz.readAll);

app.post('/api/rides', biz.create);

app.get( '/api/rides/:id', biz.read);

app.put( '/api/rides/:id', biz.update);

app.delete('/api/rides/:id', biz.delete);


// 서버를 구동한다

app.listen(8080, function () {

    console.log("Express server listening on port %d in %s mode", 8080, app.settings.env);

});



3) RESTful API 테스트하기 

  - Chrome extention 인 "Advanced REST client" 설치한다 : Chrome Web Store에서 검색하고 설치하면 됨


  - Advanced REST client 실행

    + POST 메소드를 선택 (GET, PUT, DELETE 순으로 실행하여 본다)

    + 하단의 Form 탭을 선택하여 key=value 를 추가한다 

    + "Send" 버튼을 클릭하면 결과가 최하단에 나온다 (200 OK)

          

 - MongoDB에서 결과 조회

$ mongo

MongoDB shell version: 2.2.3

connecting to: test

> show dbs

local (empty)


// POST가 성공하면 데이터베이스가 보인다 

> show dbs

local (empty)

ride_database 0.203125GB

> use ride_database

switched to db ride_database

> show collections

rides

system.indexes

> db.rides.find().toArray();

[

{

"title" : "투어 드 코리아",

"rider" : "윤복자",

"ridingDate" : ISODate("2013-06-01T00:00:00Z"),

"keywords" : "코리아 싸이클링",

"_id" : ObjectId("5142ccbd1b06d36acc000001"),

"__v" : 0

}

]

>


  - PUT 은 URL 뒤에 MongoDB document의 _id 값인  5142ccbd1b06d36acc000001  을 붙여준다. (DELETE 도 동일)

   



4) Backbone.js 와 Node.js 연결하기 

  - 클라이언트 <-> 서버 연결

    + MongoDB사용시 : _id 지정하기 

    + url 설정 : /api/rides

    + fetch() 호출

    + reset callback으로 render 설정

  - ride.js 백본파일을 수정 : 라이딩 정보 목록 가져오기 == 컬렉션 모델에서 url 설정

   var Ride = Backbone.Model.extend({

        defaults:{

            coverImage:"img/my_cycle.png",

            title:"2011년 대관령 대회",

            rider:"Yun YoungSik",

            ridingDate:"2011",

            keywords:"대관령 힐크라이밍 대회"

        },

        idAttribute: "_id"  // MongoDB 사용시 바꾸어줌 

        // idAttribute 와 같은 효과를 같는다 

        /*parse:function (response) {

            console.log(response);

            response.id = response._id;

            return response;

        }*/

    });

.. 중략 ..  

    // rides 

   /* var rides = [{title:"대관령 힐크라이밍 대회", rider:"Yun YoungSik", ridingDate:"2010", keywords:"대관령"},

        {title:"미시령 힐크라이밍 대회", rider:"Yun DoWon", ridingDate:"2011", keywords:"미시령"},

        {title:"투어 드 코리아", rider:"Yun YoungSik", ridingDate:"2012", keywords:"코리아"}];*/

    var rides = [];   // 위의 샘플은 사용하지 않고 빈 배열을 만들어 준다 

  

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

    // Ride Collection

    var Rides = Backbone.Collection.extend({

        model : Ride,

        url : '/api/rides'  // 서버 호출 API

    });


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

    // Collection View

    var RidesView = Backbone.View.extend({

        el: $("#rides"),


        initialize: function(){

            this.collection = new Rides(rides);

            // 서버로 부터 데이터 가져오기 

            this.collection.fetch();

            this.render();

            // 컬렉션 add가 호출되면 renderRide를 trigger 한다 

            this.collection.on("add", this.renderRide, this);

            this.collection.on("remove", this.removeRide, this);

            // fetch()가 비동기적이기 때문에 Backbone이 reset을 호출하면 trigger 될 수 있도록 한다 

            this.collection.on("reset", this.render, this);  

        },

.. 중략 ..


  - 브라우져 호출 : MongoDB 의 데이터를 가져와서 Backbone이 데이터를 reset 하게 되면 입력한 라이딩정보가 출력된다 

   


  -  날짜가 이상하게 나오므로 jQuery를 이용하여 날짜를 포멧팅 해준다 

<script id="rideTemplate" type="text/template">

        <img src="<%= coverImage %>"/>

        <ul>

            <li><%= title %></li>

            <li><%= rider %></li>

            <!-- 년/월/일 만 표현 --> 

            <li><%= $.format.date(new Date(ridingDate), 'yyyy/MM/dd') %></li>

            <li><%= keywords %></li>

        </ul>

        <button class="delete">Delete</button>

    </script>

</div>

<script src="js/jquery.js"></script>

<!-- 다운로드 받아서 js 폴더에 놓는다 -->

<script src="js/jquery-dateformat.js"></script>

<script src="js/underscore.js"></script>

<script src="js/backbone.js"></script>

<script src="js/ride.js"></script>


  - 브라우져 화면에서 MongoDB로 add 하기 : Backbone의 ride.js 에서 addRide 수정

var RidesView = Backbone.View.extend({

        el: $("#rides"),


        initialize: function(){

            this.collection = new Rides(rides);

            // 서버로 부터 데이터 가져오기 

            this.collection.fetch();

            this.render();

            // 컬렉션 add가 호출되면 renderRide를 trigger 한다 

            this.collection.on("add", this.renderRide, this);

            this.collection.on("remove", this.removeRide, this);

            // fetch()가 비동기적이기 때문에 Backbone이 reset을 호출하면 trigger 될 수 있도록 한다 

            this.collection.on("reset", this.render, this);

        },


        render: function(){

            var that = this;

            _.each(this.collection.models, function(item){

                that.renderRide(item);

            }, this);

        },


        renderRide: function(item){

            var rideView = new RideView({

                model: item

            });

            this.$el.append(rideView.render().el);

        },


        addRide: function(e){

            e.preventDefault();


            var formData = {};

            // jQuery의 each로 formData key=value 객체를 만듦

            $("#addRide").children("input").each(function (i, el) {

                if ($(el).val() !== "") {

                    formData[el.id] = $(el).val();

                }

            });


            // rides 배열에 저장

            rides.push(formData);

            // 컬렉션 객체에 저장

            // 서버로 저장하기 기존 add를 create 으로 바꾸면 된다 

            this.collection.create(formData); 

            //this.collection.add(new Ride(formData));

        }, 


  - 브라우져 결과 화면 : "평택 대회" 입력

   


  - MongoDB의 결과값 : 3개의 도큐먼트가 존재한다 

> db.rides.find().toArray();

[

{

"title" : "투어 드 코리아3",

"rider" : "윤영식",

"ridingDate" : ISODate("2012-06-01T00:00:00Z"),

"keywords" : "싸이클링 코리아 우승",

"_id" : ObjectId("5142dc4ae196a916e0000001"),

"__v" : 0

},

{

"title" : "대관령 힐크라이밍 대회",

"rider" : "윤도원",

"ridingDate" : ISODate("2013-07-01T00:00:00Z"),

"keywords" : "강원도 우승목표",

"_id" : ObjectId("5142dd8cba03b734e1000001"),

"__v" : 0

},

{

"title" : "평택 대회",

"rider" : "윤복자",

"ridingDate" : ISODate("2013-03-01T00:00:00Z"),

"keywords" : "고속도로 개통기념",

"_id" : ObjectId("5142de7351b4341ee2000001"),

"__v" : 0

}

]

>


  - 원문에 포함된 날짜 포함하기와 키워드배열로 만들기는 패스~~~ ^^;


** 전체 소스 코드 

riding.zip



<참조>

  - 원문 : Backbone.js Developing 번역글

  - Node.js & Express & Mongoose ToDo 예제

  - Handlebars 홈페이지

  - Tool : Advanced Rest Client

posted by 윤영식
2013. 3. 15. 13:30 Backbone.js

Backbone.js 서점 샘플 예제를 사이클링 샘플로 변경하면서 직접 코딩해 보고 구조를 파악하자. 



1) index.html 파일 준비하기 

  - 이미지 파일 다운로드 하고 img 폴더에 넣는다 (100*152)

   


  - backbone.js, underscore.js, jquery.js 파일 다운로드 받아서 js 폴더에 넣는다 

   


  - index.html 파일을 루트에 생성한다


  - 브라우져 수행 점검 

   


  - css 폴더를 만들고 screen.css 파일을 생성한다 

css body { background-color: #eee; }

.bookContainer { border: 1px solid #aaa; width: 350px; height: 170px; background-color: #fff; float: left; margin: 5px; } .bookContainer img { float: left; margin: 10px; } .bookContainer ul { list-style-type: none; }



2) index.html 확장하기 

  - script 태그를 넣는다 

<script id="rideTemplate" type="text/template">

        <img src="<%= coverImage %>"/>

        <ul>

            <li><%= title %></li>

            <li><%= rider %></li>

            <li><%= ridingDate %></li>

            <li><%= keywords %></li>

        </ul>

    </script>

   Model의 명칭과 template의 <%= 명칭 %> 을 일치시켰다

   


  

3) ride.js 통해 Backbone 코딩하기 

  - js 폴더안에 ride.js파일을 생성하고 Model, View 클래스를 만든다 

(function ($) {

    // model 만들기

    var Ride = Backbone.Model.extend({

        defaults:{

            coverImage:"img/my_cycle.png",

            title:"2011년 대관령 대회",

            rider:"Yun YoungSik",

            ridingDate:"2011",

            keywords:"대관령 힐크라이밍 대회"

        }

    });


    // view 만들기

    var RideView = Backbone.View.extend({

        // el 변수에 html에 있는 DOM 레퍼런스를 만든다. $el은 DOM에 대한 jQuery 객체를 의미 한다  

        // html에 없는 DOM을 만들때는 tagName/className/id 등을 설정한다. 설정하지 않으면 기본 div 태그임

        // el 이란 무엇인가?

        tagName:"div",

        // screen.css 에서 사용할 css의 클래스이름 .rideContainer 

        className:"rideContainer",

        template:$("#rideTemplate").html(),


        render:function () {

            //tmpl은 JSON객체를 받아서 html을 반환하는 함수이다.

            var tmpl = _.template(this.template); 

            //jQuery html() 함수를 사용하기 위해 jQuery객체 $el을 쓴다.

            this.$el.html(tmpl(this.model.toJSON())); 

            return this;

        }

    });


})(jQuery);


  - View를 생성하고 Model를 할당한 후 render()를 호출하여 화면을 그린다 

(function ($) {

    // model 만들기

    var Ride = Backbone.Model.extend({

        defaults:{

            coverImage:"img/my_cycle.png",

            title:"2011년 대관령 대회",

            rider:"Yun YoungSik",

            ridingDate:"2011",

            keywords:"대관령 힐크라이밍 대회"

        }

    });


    // view 만들기

    var RideView = Backbone.View.extend({

        tagName:"div",

        className:"rideContainer",

        template:$("#rideTemplate").html(),


        render:function () {

        //tmpl은 JSON객체를 받아서 html을 반환하는 함수이다.

            var tmpl = _.template(this.template); 

            //this.el은 tagName에 정의된 것이다. jQuery html() 함수를 사용하기 위해서는 $el을 쓴다.

            this.$el.html(tmpl(this.model.toJSON())); 

            return this;

        }

    });


   // model, view 생성하여 view에 model 할당하기 

    var ride = new Ride({

        title:"Some title",

        rider:"Yun DoWon",

        ridingDate:"2011",

        keywords:"대관령 힐크라이밍 대회"

    });


    rideView = new RideView({

        model: ride

    });

    // 템플릿에 값을 맵핑한 DOM 을 최종적으로 넘김 

    $("#rides").html(rideView.render().el);

    

})(jQuery);


  - 브라우져 호출 결과화면 

    



4) Collection을 통해 목록 만들기 

 - Collection 클래스를 상속받아 Collection Model을 만들고 Collection을 제어할 수 있는 View도 만든다 

(function ($) {


    var Ride = Backbone.Model.extend({

        defaults:{

            coverImage:"img/my_cycle.png",

            title:"2011년 대관령 대회",

            rider:"Yun YoungSik",

            ridingDate:"2011",

            keywords:"대관령 힐크라이밍 대회"

        }

    });


    var RideView = Backbone.View.extend({

        tagName:"div",

        className:"rideContainer",

        template:$("#rideTemplate").html(),


        render:function () {

        //tmpl은 JSON객체를 받아서 html을 반환하는 함수이다.

            var tmpl = _.template(this.template); 

            //this.el은 tagName에 정의된 것이다. jQuery html() 함수를 사용하기 위해서는 $el을 쓴다.

            this.$el.html(tmpl(this.model.toJSON())); 

            return this;

        }

    });


    var ride = new Ride({

        title:"Some title",

        rider:"Yun DoWon",

        ridingDate:"2011",

        keywords:"대관령 힐크라이밍 대회"

    });


    var rideView = new RideView({

        model: ride

    });


    $("#rides").html(rideView.render().el);

    

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

    // rides 샘플 3개 생성

    var rides = [{title:"대관령 힐크라이밍 대회", rider:"Yun YoungSik", ridingDate:"2010", keywords:"대관령"},

        {title:"미시령 힐크라이밍 대회", rider:"Yun DoWon", ridingDate:"2011", keywords:"미시령"},

        {title:"투어 드 코리아", rider:"Yun YoungSik", ridingDate:"2012", keywords:"코리아"}];


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

    // Ride Collection 생성

    var Rides = Backbone.Collection.extend({

        model : Ride

    });


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

    // Collection View 생성

    var RidesView = Backbone.View.extend({

        el:$("#rides"),


        initialize:function(){

            this.collection = new Rides(rides);

            this.render();

        },


        // 컬렉션의 내역을 루핑을 돌고, 

        // each 메소드내에서서 함수 그자체가 this가 된다. 따라서 this 참조 안하게 that 을 설정한다 (블로깅)

        render: function(){

            var that = this;

            _.each(this.collection.models, function(item){

                that.renderRide(item);

            }, this);

        },


        // RideView 를 통해 개별적으로 렌더링한다 

        renderRide:function(item){

            var rideView = new RideView({

                model: item

            });

            this.$el.append(rideView.render().el);

        }

    });


    // new 를 하면  initialize() 가 자동 호출된다 

    var ridesView = new RidesView();


})(jQuery);


  - 호출한 결과 화면 : 기존 1개와 샘플 데이터 4개로 총 4개의 화면이 나옴 

   



5) 컬렉션에 모델을 추가하기 

  - 옷 신제품 발표 컬렉션에 옷입은 모델들을 추가하는 느낌이랄까 어감이 비슷하네요. ^^

  - 모델을 추가하기 위해서 index.html에 input tag 를 추가한다 

.. 중략 ..

<div id="rides">

    <!-- default 로 들어 있던 div 태그 제거 --> 

    <!--div class="rideContainer">

        <img src="img/my_cycle.png"/>

        <ul>

            <li>Title</li>

            <li>Rider</li>

            <li>Riding date</li>

            <li>Keywords</li>

        </ul>

    </div-->


    <!-- 새로운 카드를 넣을 수 있는 input tags --> 

    <div id="addRide">

        <label for="coverImage">CoverImage: </label><input id="coverImage" type="file" />

        <label for="title">Title: </label><input id="title" type="text" />

        <label for="rider">Rider: </label><input id="rider" type="text" />

        <label for="ridingDate">Riding date: </label><input id="ridingDate" type="text" />

        <label for="keywords">Keywords: </label><input id="keywords" type="text" />

        <button id="add">Add</button>

    </div>


    <!-- 하단의 template script 만 놓는다 --> 

    <script id="rideTemplate" type="text/template">

.. 중략 ..


  - screen.css 파일에 하기 내역을 추가한다 

css body { background-color: #eee; }


.rideContainer { border: 1px solid #aaa; width: 350px; height: 170px; background-color: #fff; float: left; margin: 5px; } .rideContainer img { float: left; margin: 10px; } .rideContainer ul { list-style-type: none; }


#addRide label {

    width:100px;

    margin-right:10px;

    text-align:right;

    line-height:25px;

}


#addRide label, #addRide input {

    display:block;

    margin-bottom:10px;

    float:left;

}


#addRide label[for="title"], #addRide label[for="ridingDate"] {

    clear:both;

}


#addRide button {

    display:block;

    margin:5px 20px 10px 10px;

    float: right;

    clear: both;

}


#addRide div {

    width: 550px;

}


#addRide div:after {

    content:"";

    display:block;

    height:0;

    visibility:hidden;

    clear:both;

    font-size:0;

    line-height:0;

}


  - ride.js 파일에서 RidesView 안의 renderRide 메소드 밑으로 addRide 메소드를 추가한다

// 테스트로 한개 넣었던 코드에 대해서 주석처리한다.

/*  var ride = new Ride({

        title:"No title",

        rider:"Unknown",

        ridingDate:"Unknown",

        keywords:"empty"

    });


    var rideView = new RideView({

        model: ride

    });


    $("#rides").html(rideView.render().el);  */

    

.. 중략 ..

        renderRide:function(item){

            var rideView = new RideView({

                model: item

            });

            this.$el.append(rideView.render().el);

        },


        addRide: function(e){

      e.preventDefault();


            var formData = {};

      // jQuery의 each로 formData key=value 객체를 만듦

             $("#addRide").children("input").each(function (i, el) {

                if ($(el).val() !== "") {

                    formData[el.id] = $(el).val();

                }

            });


            // rides 배열에 저장

            //rides.push(formData);

            // 컬렉션 객체에 저장 

            this.collection.add(new Ride(formData));

        }, 

        

        // add버튼 누를때 이벤트 발생하여 addRide 메소드 호출

        events:{

            "click #add": "addRide"

        }


    });


  - 컬렉션에 모델이 추가되는 add 메소드 호출시 이벤트 발생토록 함

var RidesView = Backbone.View.extend({

        el:$("#rides"),


        initialize:function(){

            this.collection = new Rides(rides);

            this.render();


// initailize 초기화일 때 자동으로 trigger 될 수 있도록 설정을 합니다 

            this.collection.on("add", this.renderRide, this);

        },


  - 실행하기 

$ static 

serving "." at http://127.0.0.1:8080

11:16:58 [200]: /

11:16:58 [200]: /css/screen.css

11:16:58 [200]: /js/ride.js

11:16:58 [200]: /js/underscore.js

11:16:58 [200]: /js/backbone.js

11:16:58 [200]: /img/my_cycle.png

11:16:58 [200]: /js/jquery.js

11:16:58 [404]: /favicon.ico


    + http://localhost:8080 호출 한 경우

   

    + 값을 입력하고 "Add" 클릭하였을 때 (평택 대회)

   



6) 모델 삭제하기 

  - index.html안에 삭제 버튼을 넣는다 

 <script id="rideTemplate" type="text/template">

        <img src="<%= coverImage %>"/>

        <ul>

            <li><%= title %></li>

            <li><%= rider %></li>

            <li><%= ridingDate %></li>

            <li><%= keywords %></li>

        </ul>

        <button class="delete">Delete</button>

    </script>


  - button에 대한 screen.css 를 조정한다 

.rideContainer ul {

    list-style-type: none;

    margin-bottom: 0;

}


.rideContainer button {

    float:right;

    margin: 10px;

}


  - 개별 모델에 delete button이 놓인다

   


  - 삭제 로직을 RideView 에 코딩한다 : 삭제 메소드 -> 이벤트 등록

     var RideView = Backbone.View.extend({

        tagName:"div",

        className:"rideContainer",

        template:$("#rideTemplate").html(),


        render:function () {

         //tmpl은 JSON객체를 받아서 html을 반환하는 함수이다.

            var tmpl = _.template(this.template); 

            //this.el은 tagName에 정의된 것이다. jQuery html() 함수를 사용하기 위해서는 $el을 쓴다.

            this.$el.html(tmpl(this.model.toJSON())); 

            return this;

        }, 


        events: {

            "click .delete": "deleteRide"

        },


        deleteRide:function () {

            //모델을 삭제한다.

            this.model.destroy();


            //뷰를 삭제한다.

            this.remove();

        }

    });



7) 컬렉션 모델 삭제하기

  - rides 배열에는 아직 그대로 데이터가 존재한다. 이를 삭제하기 위하여 먼저 remove 이벤트의 trigger를 RidesView에 등록한다

var RidesView = Backbone.View.extend({

        el: $("#rides"),


        initialize: function(){

            this.collection = new Rides(rides);

            this.render();

            // 컬렉션 add가 호출되면 renderRide를 trigger 한다 

            this.collection.on("add", this.renderRide, this);

            this.collection.on("remove", this.removeRide, this);

        },


        render: function(){

            var that = this;

            _.each(this.collection.models, function(item){

                that.renderRide(item);

            }, this);

        },


        renderRide: function(item){

            var rideView = new RideView({

                model: item

            });

            this.$el.append(rideView.render().el);

        },


        addRide: function(e){

            e.preventDefault();


            var formData = {};

            // jQuery의 each로 formData key=value 객체를 만듦

            $("#addRide").children("input").each(function (i, el) {

                if ($(el).val() !== "") {

                    formData[el.id] = $(el).val();

                }

            });


            // rides 배열에 저장

            rides.push(formData);

            // 컬렉션 객체에 저장 

            this.collection.add(new Ride(formData));

        }, 


        // 삭제된 모델을 인자로 자동 넣어준다 

        removeRide: function(removedRide){

            // attributes는 Model의 hash key=value object 이다 

            var removedRideData = removedRide.attributes;


            _.each(removedRideData, function(val, key){

                if(removedRideData[key] === removedRide.defaults[key]){

                    console.log(">> 1 : " + removedRideData[key]);

                    delete removedRideData[key];

                }

            });


            _.each(rides, function(ride){

                if(_.isEqual(ride, removedRideData)){

                    console.log(">> 2 : " + ride);

                    rides.splice(_.indexOf(rides, ride), 1);

                }

            });

        },

        

        // add버튼 누를때 이벤트 발생하여 addRide 메소드 호출

        events:{

            "click #add": "addRide"

        }

    });


  - 삭제버튼을 클릭하면 삭제가 잘 된다 



* 지금까지의 소스 

riding.zip



<참조>

  - 원문 : Backbone.js Developing 번역글

posted by 윤영식
2013. 3. 14. 15:05 Dev Environment/Mac, Dev Tools

github에서 쓰는 위크 포멧으로 마크다운을 많이 사용하고 있고, README.md파일을 해석된 UI 형태로 보고 싶을 경우 QuickLook을 통하여 설정하는 방법을 알아보자 



1) 설치하기

  - 플러그인 다운로드

  - 자신의 계정/Library로 이동. 만일, QuickLook 폴더가 없다면 직접 만든다 : ~/Library/QuickLook

  - 다운받은 .zip 파일 압축해제하고 전체 파일을 ~/Library/QuickLook 폴더에 copy 한다 



2) 사용하기 

  - Finder에서 *.md 을 선택하면 플러그인 설치전에는 Markdown 포멧으로 나오던 것이 해석된 형태로 보인다

   


  - 팝업창으로 보고 싶다면 commnad + y 키를 사용한다. 창닫기는 escape 키 이다 



<참조>

  - QuickLook 플러그인 설치하기 

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. 13. 16:58 Testing, TDD/Test First

Backbone.js 번역서를 보던 중 코드를 테스트 해볼 수 있는 방법을 찾아 보았다. 백본이 브라우져에서 구동되는 SPA 라는 점을 감안한다면 브라우져 없이도 테스트 가능케 하는 방법으로 Casper.js 가 괜찮은 대안으로 보였다.  알아 볼까나~~~



1) Casper.js 란 무엇인가?

  - 클라이언트 사이드 화면에 대하여 PhatomJS 기반하에 자바스크립트 코드로 테스트를 좀 더 쉽게 만들어 주는 유틸리티이다

  - 따라서 사전에 PhantomJS는 설치되어 있어야 한다 (v1.7 이상, $ phantomjs --version 으로 확인)

  - UI 코드 테스팅이 가능하다 : 페이지 네비게이션, 이벤트, 폼 서밋, 화면 렌더링 이미지 캡쳐등이 가능 

  - 즉, 브라우져에서 사람이 하는 행위를 자동화 하여 수행할 수 있다

    


2) 설치하기 

  - GitHub 위치 : https://github.com/n1k0/casperjs

  - Mac : /usr/local/bin 은 .bash_profile 안에 PATH 걸려 있다는 가정

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

// phantomjs 먼저 설치

1) 웹사이트에서 zip 파일 다운로드 

2) 심볼릭 링크 : phantomjs 디렉토리로 들어가서 설정한 것임 

$ ln -sf `pwd`/bin/phantomjs /usr/local/bin/phantomjs


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

// casperjs 설치 

$ git clone https://github.com/n1k0/casperjs.git

$ cd casperjs/

$ git checkout tags/1.0.2

ln -sf `pwd`/bin/casperjs /usr/local/bin/casperjs



3) 사용하기

  - PhantomJS를 내부적으로 사용하기 때문에 팬텀의 기본 사용법을 익힌다  

    + 팬텀 v1.6 이상부터 자체 내장 모듈을 4가지를 가지고 있다. CommonJS Modules' Require (참조)

       1. webpage : var page = require('webapage').create(); 웹페이지 핸들링 추상화

       2. system : var system = require('system'); os 정보, pid 정보, cli 아규먼트 정보

       3. fs : var fs = require('fs'); 파일 시스템 핸들링, 디렉토리, 패스, 이름 정보

       4. webserver : var svr = require('webserver').create(); Mongoose 라는 자체 웹서버 내장


    + 여러 가지 예제들을 돌려보자 (예제 소스)


  - PhantomJS 간단 예제 

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

// 테스트하고 빠져나오기 

// hello.js 코드

console.log("hi dowon");

phantom.exit();


// 결과 

$ phantomjs hello.js

hi dowon



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

// URL 속도 측정

// loadspeed.js

var page = require('webpage').create(),

    system = require('system'),

    t, address;


if (system.args.length === 1) {

    console.log('Usage: loadspeed.js <some URL>');

    phantom.exit();

}


t = Date.now();

address = system.args[1];

page.open(address, function (status) {

    if (status !== 'success') {

        console.log('FAIL to load the address');

    } else {

        t = Date.now() - t;

        console.log('Loading time ' + t + ' msec');

    }

    phantom.exit();

})


// 결과 

$ phantomjs loadspeed.js http://www.google.co.kr

Loading time 1027 msec


  - CasperJS API의 예제를 돌려보자  

    + Casper API : var capser = require('casper').create(); 캐스퍼 생성, 로깅, 시작/종료/대기, 마우스이벤트, 페이지네비게이션

    + Client Side API : 에코, 인코딩, 북마크릿(웹킷기반 브라우져만), 폼/필드 값얻기(__utils__ 프로퍼티사용), 마우스 이벤트 

    + Colorizer API : var colorizer = require('colorizer').create('Colorizer'); 레벨에 따른 출력 색깔 표현

        casper.echo('text message', 'INFO'); // green

        casper.echo('text message', 'ERROR'); // red 

        미리 정의된 레벨과 컬러값 

        


    + Tester API :casper객체에 test 프로퍼티로 Tester 클래스 제공함. assert 계열 메소드, info 메세지 

var url = 'http://www.google.fr/';

var casper = require('casper').create();

casper.start(url, function() {

    this.test.assert(this.getCurrentUrl() === url, 'url is the one expected');

});

   + Utils API var util = require('utils')표준 자바스크립트 API 없는 것들 보충. isXX() 계열 메소드, dump하여 JSON 출력등 


  - CasperJS 간단 예제 

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

// casperjs 사용하여 이미지 생성

// 객체 생성

var casper = require('casper').create({

    // 옵션적용

    onError: function(self, m) {   // Any "error" level message will be written

        console.log('FATAL:' + m); // on the console output and PhantomJS will

        self.exit();               // terminate

    }

});


// 지정 url 호출과 callback 지정 

// start 대신 open 을 통하여 POST, PUT, DELETE 값 전달도 가능 

casper.start('http://www.weather.com/', function() {

    this.captureSelector('weather.png', '#wx-main');

});


// 캐스퍼 수행하기  

casper.run();


// casper.run() 이 Async 구동이기 때문에 casper.exit(); 를 호출하면 이미지 생성전에 exit 됨 (호출하지 말것)

//casper.exit();  



4) Backbone.js 테스트 절차

나름 어떻게 테스트 해 볼 수 있을까 고민하다가 다음의 순서로 해보았고, 코드에 assert을 넣거나 응용을 하면 될듯하다  


  - 먼저 테스트 클라이언트 화면을 만든다 (index.html)

  - CasperJS 테스트 코드를 짠다 

  - 테스트를 위한 웹서버를 구동한다 (static 띄우기)

$ static

serving "." at http://127.0.0.1:8080

16:46:17 [200]: /index.html

16:46:17 [200]: /lib/jquery-1.8.2.min.js

16:46:17 [200]: /lib/backbone-min.js

16:46:17 [200]: /lib/underscore-min.js


  - 브라우져에서 확인(옵션)


  - CasperJS 테스트 수행하기 

$ casperjs  index_test.js

>> {"title":"","completed":false}

test done



5) Node 에서 단순 테스트 하기 

  - 백본을 노드에서 수행할 수도 있다. 어차피 자바스크립트이기 때문이다

  - 기본준비 : underscore.js, backbone.js 모듈을 node_modules 디렉토리방식으로 설치한다 

$ npm install underscore

$ npm install backbone


  - coffeescript 예제 코드 만들기 

Backbone = require 'backbone'


Todo = Backbone.Model.extend {

    # Todo에 대한 기본 속성

    defaults: {

      title: '',

      completed: false

    }

  }


Todos = Backbone.Collection.extend {

    model: Todo

  }


firstTodo = new Todo {title:'Read whole book'}


# 컬렉션 객체에 모델 배열을 전달한다.

todos = new Todos [firstTodo]

console.log todos.length


  - 수행 결과

// 커피파일 컴파일 

$ coffee -c *.coffee


// 노드로 수행 

$ node backbone_node.js

1



<참조>

  - Casper.js 홈페이지

  - NodeQA 사이트 소개글

  - Headless Integration Tests with CasperJS


posted by 윤영식
2013. 3. 12. 17:41 카테고리 없음

Sublime Text 2에서 Node.js 수행을 위한 Path 설정하기 


1) 설정 

  - Ctrl + Shift + P => "Nodejs::Default File Settings" 검색하여 환경파일 오픈 (Node.js.sublime-settings)

// 기본 설정 내역

{

  // save before running commands

  "save_first": true,

  // if present, use this command instead of plain "node"

  // e.g. "/usr/bin/node" or "C:\bin\node.exe"

  "node_command": false,

  // Same for NPM command

  "npm_command": false,


  "expert_mode": false,


  "ouput_to_new_tab": false

}


// 변경 설정 내역 

{

  // save before running commands

  "save_first": true,

  // if present, use this command instead of plain "node"

  // e.g. "/usr/bin/node" or "C:\bin\node.exe"

  "node_command": "/usr/local/bin/node",

  // Same for NPM command

  "npm_command": false,


  "expert_mode": false,


  "ouput_to_new_tab": false

}


2) 실행

  - Ctrl + R 



<참조> 

  - 원문 http://stackoverflow.com/questions/12124544/can-not-run-node-app-with-nodejs-sublime-plugin

posted by 윤영식
2013. 3. 12. 17:31 Dev Environment/Sublime Text

Sublime Text에서 코딩한 코드를 Command Line으로 수행하고 싶을 경우 Terminal을 hot key로 띄워서 수행 할 수 있다. 


  - 참조 : Terminal을 띄우기 위한 설정

  - Install Package에서 "Terminal"이라고 타입핑하고 설치한다 

    + mac : command + shift + p  => Install Package => Terminal 입력

// git clone 을 통하여 sublime_terminal을 다운로드 받는다 

/Users/xxx/Applications> git clone https://github.com/wbond/sublime_terminal.git

Cloning into 'sublime_terminal'...

Unpacking objects: 100% (99/99), done.


// Sublime Text > Preferences > Package Settings > Terminal > Settings Defaults 에서 iTerm.sh를 설정한다 

// iTerm.sh 연결

{

// The command to execute for the terminal, leave blank for the OS default

// On OS X the terminal can be set to iTerm.sh to execute iTerm

"terminal": "/Users/nulpulum/Applications/sublime_terminal/iTerm.sh",


// A list of default parameters to pass to the terminal, this can be

// overridden by passing the "parameters" key with a list value to the args

// dict when calling the "open_terminal" or "open_terminal_project_folder"

// commands

"parameters": []

}

  

  - 소스 코드를 저장하고 ctrl + shift + t 를 클릭하면 코드가 있는 Path로 Terminal이 열린다 


<참조>

  - 소스 : https://github.com/wbond/sublime_terminal

posted by 윤영식
2013. 3. 12. 17:14 Dev Environment/Sublime Text

Mac OS에서 Sublime Text 2 를 Terminal 에서 바로 수행시키는 방법


1) 설정

// 심볼릭 링크 걸기

$ ln -s /Applications/Sublime\ Text\ 2.app/Contents/SharedSupport/bin/subl /usr/local/bin/sublime


// 로그인 계정에 PATH 설정

$ vim .bash_profile 

export PATH=/usr/local/bin:{...}



2) 수행하기 

  - 파일 열기 : sublime <filename>

  - 특정 디렉토리 열기 : sublime <directory name>

  - 현재 디렉토리 열기 : sublime



<참조>

  - 원문 https://gist.github.com/artero/1236170

posted by 윤영식
2013. 3. 12. 13:55 Backbone.js

MVC 프레임워크를 사용하는 이유는 마틴 파울러 아저씨의 주장처럼 Clean Code를 생산하기 위해서이다. 클린 코드가 되면 클라이언트 입장에서 events와 callbacks의 유지보수 용이성이 높아지고, Testing이 쉬워진다. Backbone.js 는 그 중에서도 가장 간결하면서 여러 Third part 모듈을 붙일 수 있어서 확장성이 높다. 그러나 Backbone.js 만을 가지고 큰 규모의 확장성 있는 애플리케이션을 만들기에는 부족함이 존재한다. 이를 해결할 수 있는 방법을 알아보자 


1) MV* Framework : Backbone.js 

  - Selector : jQuery, YUI 등 선택

  - Template : Handlebar, Mustache 등 선택

  - MVC

    + Model : Data 유효성검사, 서버로부터 가져오고, 여러 화면에 공유하는 백본의 핵심 - Collection 개념으로 확장

    + View : 데이터의 표현, Cotroller 역할 하지 않고  EventListener를 통하여 Controller에게 위임한다 

    + Controller :  백본에선 진정한 Controller 는 없다. View & Router 가 controller와 유사하고, Router는 Model 이벤트의 요청에 대해 View 응답을 갖는다 

  - 백본은 SPA(Single Page Applications) 개발을 위한 기본적인 추상화 레이어(Abstraction Layer)일 뿐이다. 모든 것을 제공하진 않으니 필요한 요소를 잘 선택하여 모듈화해야 한다 



2) 좀 더 복잡한 SPA 개발하기 

  - 백본을 기반으로 Large Scale Application 개발을 위한 프레임워크들

  - http://chaplinjs.org/ 

  - http://thoraxjs.org/ : Backbone.js & Handlebar 

  - http://marionettejs.com/

  - Backbone.js LayoutManager

  - Aura : 위젯기반 프레임워크 



3) 채플린 배우기 

  - 백본은 저수준의 프레임워크이기 때문에 잘 구조화해서 사용하는 방법을 알아야 한다 

  - 백본 예제의 To do list 는 SPA 라고 할 수 없으며 좀 더 규모가 큰 구조화된 애플리케이션 개발의 가이드가 될 수 없다 

  - 예제 : 채플린 기반 Real world application 인 moviepilot

  - GitHub 저장소

  - 의존성 (SmartSolution에서 사용할 기준)

    + Backbone.js

    + Underscore.js

    + jQuery

    + Handlebar.js 

    + AMD (Requires.js)

    + CoffeeScript

  - 채플린 Boilerplate 소스



4) 채플린 Boilerplate 소스 돌려보기 

  - Git clone

$ git clone https://github.com/chaplinjs/chaplin-boilerplate.git

Cloning into 'chaplin-boilerplate'...

remote: Counting objects: 263, done.

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

remote: Total 263 (delta 135), reused 178 (delta 50)

Receiving objects: 100% (263/263), 302.91 KiB | 148 KiB/s, done.

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


  - static 수행 (블로깅)

static

serving "." at http://127.0.0.1:8080


// 브라우져 호출


// 브라우져 호출후 static console 출력 내용 

11:28:08 [200]: /

11:28:08 [200]: /js/vendor/require-2.1.1.js

11:28:08 [200]: /js/hello_world_application.js

11:28:08 [200]: /js/routes.js

11:28:08 [200]: /js/vendor/chaplin-0.6.0.js

11:28:08 [200]: /js/views/layout.js

11:28:08 [200]: /js/vendor/jquery-1.8.3.js

11:28:08 [200]: /js/vendor/underscore-1.4.3.js

11:28:09 [200]: /js/vendor/backbone-0.9.9.js

11:28:09 [200]: /js/controllers/hello_world_controller.js

11:28:09 [200]: /js/models/hello_world.js

11:28:09 [200]: /js/views/hello_world_view.js

11:28:09 [200]: /js/models/base/model.js

11:28:09 [200]: /js/views/base/view.js

11:28:09 [200]: /js/vendor/require-text-2.0.3.js

11:28:09 [200]: /js/lib/view_helper.js

11:28:09 [200]: /js/templates/hello_world.hbs

11:28:09 [200]: /js/vendor/handlebars-1.0.rc.1.js

11:28:09 [200]: /js/lib/utils.js

11:28:09 [404]: /favicon.ico


  - .coffee 소스 수정시 컴파일 방법

    + /coffee 디렉토리에 위치 /js 컴파일된 파일 

    + 컴파일 : coffee --bare --output js/ coffee/


  - 구조파악 

    + models, views, controllers, lib, vendor  디렉토리

    + vendor : backbone.js, jquery.js 등의 모듈 

    + views : Handlebar 템플릿 사용 -> js/templates/hello_world.hbs 

    + index.html : AMD 모듈 관리 (블로깅) -> coffee/hello_world_application.coffee 메인

   


5) 마리오네트

  - 다음 블로깅에서 마리오네트를 살펴보자 : 좀 더 활발하게 활동하고 있다는데 채플린 또는 마리오네트 아니면 코너스톤중 택일



<참조>

  - 채플린 (Chaplin)

  - JavaScript Framework 별 To Do List

  - 예제 : 와인셀러 (블러깅)

  - 채플린 Boilerplate 소스

  - 코너스톤 팀의 Backbone & CSS 를 위한 프레임워크 고도화


posted by 윤영식
2013. 3. 11. 17:13 Backbone.js

Backbone.js를 익히기 전에 Underscore에 대해서 알아보자.



1) Underscore

  - Functional Programming을 위한 자바스크립트 유틸리티 라이브러리 

  - 80 개의 펑션을 가지고 있다 

  - Groc 포멧의 소스코드 보기

  - GitHub Underscore.js

  - var _  변수 사용



2) 테스트 돌려보기 

  - GitHub에서 다운로드 : git clone https://github.com/documentcloud/underscore.git

  - package.json 내역

{

  "name" : "underscore",
  "description" : "JavaScript's functional programming helper library.",
  "homepage" : "http://underscorejs.org",
  "keywords" : ["util", "functional", "server", "client", "browser"],
  "author" : "Jeremy Ashkenas <jeremy@documentcloud.org>",
  "repository" : {"type": "git", "url": "git://github.com/documentcloud/underscore.git"},
  "main" : "underscore.js",
  "version" : "1.4.4",
  "devDependencies": {
    "phantomjs": "1.8.1-3"
  },
  "scripts": {
    "test": "phantomjs test/vendor/runner.js test/index.html?noglobals=true"
  }
}


  - scripts 의 test 에서 phantomjs 사용함 : 참조에서 phantomjs를 설치한다 (웹브라우져 없이 테스트시 많이 사용)

    + phantomjs 를 이용한 테스트 코드 작성 연구에 도움이 되겠다 

    + 테스트 코드는 underscore/test 디렉토리에 존재함 

$ npm test


> underscore@1.4.4 test /Users/nulpulum/git-repositories/underscore

> phantomjs test/vendor/runner.js test/index.html?noglobals=true


2013-03-11 13:58:26.100 phantomjs[32711:f07] *** WARNING: Method userSpaceScaleFactor in class NSView is deprecated on 10.7 and later. It should not be used in new applications. Use convertRectToBacking: instead.

Took 2309ms to run 588 tests. 588 passed, 0 failed.

  


3) Collection Functions

  - 테스트 코드

    + 모듈 설치하기 : npm install underscore 

  - 테스트 결과 

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

// 샘플 코드 짜고 node 로 결과 확인하기 

$ node collection.js

-------each  : 개별 요소를 지정한 function에서 수행

1 0 [ 1, 2, 3 ]

2 1 [ 1, 2, 3 ]

3 2 [ 1, 2, 3 ]

-------map : 각 요소를 지정한 callback에서 수행

3

6

9

-------reduce : 리듀싱

6

-------find : callback에서 찾는 즉시 break

2

-------filter : callback에 매칭되는 것만 

[ 2, 4, 6 ]

-------reject : callback에 매칭되지 않는 것만 

[ 1, 3, 5 ]

-------every : 모두가 지정한 값일 경우 참

false

-------some : 하나라도 지정한 값일 경우 참

true

-------contains : 포함하면 참

true

-------invoke : 펑션 호출 

[ [ 1, 5, 7 ], [ 1, 2, 3 ] ]

-------pluck : 지정한 프러퍼티만 뽑아냄

[ 'dowon', 'haha', 'youngsik' ]

-------max : 지정한 프로퍼티중 최대인 것

{ name: 'youngsik', age: 99 }

-------min : 지정한 플터피중 최소인 것 

3

-------groupBy : 그룹핑 

{ '1': [ 1.3, 1.9 ], '2': [ 2.1, 2.4 ] }

-------countBy : callback 계산된 그룹핑 건수 

{ odd: 3, even: 2 }

-------toArray : 배열 리턴

[ 2, 3, 4 ]

-------size : 크기 

3

** 기타 몇가지 테스트 안한 API : where, findWhere, sortBy, shuffle 등



4) Array Functions

  - 테스트 코드 

  - 테스트 결과

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

// 테스트 코드를 작성하면서 nodemon 을 통하여 결과값을 확인한다

$ nodemon array.js

------first : 배열 첫번째 요소

5

------initial : 마지막 요소 제외한 배열

[ 5, 4, 3, 2 ]

------last : 배열 마지막 요소 

1

------rest : 첫번째 요소 제외한 요소 

[ 4, 3, 2, 1 ]

------compact : '' 0 false null등을 제외한 요소만 

[ 4, 2 ]

------flatten : 1차원 배열로

[ 1, 2, 3, 4 ]

------without : 지정한 요소를 제거한 배열 

[ 1, 1, 3, 4 ]

------union : 중복 제거 

[ 1, 2, 3, 101, 11 ]

------intersection : 서로 중복되는 것만 

[ 1, 2 ]

------uniq : 유니크한 것만 

[ 1, 2, 3, 4, 5 ]

------zip : 각 배열의 요소끼리 2차원배열

[ [ 'a', 1, true ], [ 'b', 2, false ], [ 'c', 4, true ] ]

------object : 2개 배열의 요소를 javascript obejct형식의 {key:value}로 변환

{ a: 1, b: 2, c: 4 }

------indexOf : 앞에서 부터 지정한 위치

2

------lastIndexOf : 뒤에서 부터 지정한 위치

4

------sortedIndex : 지정한 값이 들어갈 위치 

2

------range : 지정한 길이 만큼 배열 만들기 

[ 0, 1, 2, 3 ]



5) Object Functions

  - 테스트 코드

  - 테스트 결과 

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

// coffee-script 로 작성하고 compile과 watch 옵션을 준다 

$ coffee --compile --watch object.coffee


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

// nodemon 으로 컴파일된 object.js 를 모니터링한다 

$ nodemon object.js 

11 Mar 16:40:06 - [nodemon] app crashed - waiting for file changes before starting...

11 Mar 16:40:14 - [nodemon] restarting due to changes...

11 Mar 16:40:14 - [nodemon] /Users/nulpulum/development/underscore/test/object.js


11 Mar 16:40:14 - [nodemon] starting `node object.js`

--------keys : 오브젝트의 키만

[ 'one', 'two', 'three' ]

--------values : 오브젝트의 값만

[ 1, 2, 3 ]

--------pairs : 각 오브젝트를 key:value 형태로 

[ [ 'one', 1 ], [ 'two', 2 ], [ 'three', 3 ] ]

--------invert : key:value를 거꾸로 

{ '1': 'one', '2': 'two', '3': 'three' }

--------functions : 객체의 모든 펑션 

[ '_',

  'after',

  'all',

  'any',

  'bind',

  'bindAll',

  'chain',

  'clone',

  'collect',

  'compact',

  'compose',

  'contains',

  'countBy',

  'debounce',

  'defaults',

  'defer',

  'delay',

  'detect',

  'difference',

  'drop',

  'each',

  'escape',

  'every',

  'extend',

  'filter',

  'find',

  'findWhere',

  'first',

  'flatten',

  'foldl',

  'foldr',

  'forEach',

  'functions',

  'groupBy',

  'has',

  'head',

  'identity',

  'include',

  'indexOf',

  'initial',

  'inject',

  'intersection',

  'invert',

  'invoke',

  'isArguments',

  'isArray',

  'isBoolean',

  'isDate',

  'isElement',

  'isEmpty',

  'isEqual',

  'isFinite',

  'isFunction',

  'isNaN',

  'isNull',

  'isNumber',

  'isObject',

  'isRegExp',

  'isString',

  'isUndefined',

  'keys',

  'last',

  'lastIndexOf',

  'map',

  'max',

  'memoize',

  'methods',

  'min',

  'mixin',

  'noConflict',

  'object',

  'omit',

  'once',

  'pairs',

  'partial',

  'pick',

  'pluck',

  'random',

  'range',

  'reduce',

  'reduceRight',

  'reject',

  'rest',

  'result',

  'select',

  'shuffle',

  'size',

  'some',

  'sortBy',

  'sortedIndex',

  'tail',

  'take',

  'tap',

  'template',

  'throttle',

  'times',

  'toArray',

  'unescape',

  'union',

  'uniq',

  'unique',

  'uniqueId',

  'values',

  'where',

  'without',

  'wrap',

  'zip' ]

--------extend : 두객체를 합침

{ name: 'dowon', age: 33 }

--------pick : 원하는 것만 끄집어 냄

{ age: 33 }

--------omit : 원하는 것을 삭제함 

{ name: 'dowon' }

--------defaults : 기본 값으로 대체 

{ flavor: 'chocolate', orange: 'lots' }

--------clone : 객체 복제하기 

{ name: 'dowon', age: 33 }

--------tap : 오브젝트 필터링시에 사용 

[ 2, 400 ]

--------has : 키가 존재하는지 체크 

true


** 그외  is** 관련 펑션이 존재함 (true/false 결과 리턴)



6) Utility Functions

  - 테스트 코드

  - 테스트 결과 

$ coffee --compile --watch utility.coffee

$ nodemon utility.js

1 Mar 17:07:19 - [nodemon] clean exit - waiting for changes before restart

11 Mar 17:08:06 - [nodemon] restarting due to changes...

11 Mar 17:08:06 - [nodemon] /Users/nulpulum/development/underscore/test/utility.js


11 Mar 17:08:06 - [nodemon] starting `node utility.js`

------noConflict : _ 언더바가 다른 모듈과 쫑나면 변경가능 

[Function]

------identity : 복사 

{ name: 'dowon' }

------times : 횟수만큼 수행

0

1

2

------random : min~max 사이값 랜덤 생성 

10

------mixin : 모듈에 펑션을 확장시킴

Youngsik

------uniqueId : 유니크 아이디를 만들어 줌

dowon_1

------escape 

dowon &amp; young &gt; hi &lt;

------unescape

dowon & young > hi <

------result : 지정된 키의 값 반환

hi

------template : 템플릿 엔진 <% ... %> 기본, {{ ... }} 등으로 변경가능

hello: dowon


------chain : 객첵를 여러 메소드에 걸쳐서 수행하고 싶을 경우 최초에 한번에 넣는다 

youngsik is 22


// 1) 과 2)는 동일한 표현이다 

1) _.map([1, 2, 3], function(n){ return n * 2; });

2) _([1, 2, 3]).map(function(n){ return n * 2; });



<참조>

  - Underscore.js 테스트 코드

  - PhantomJS 설치하기

  - package.json의 test 스크립트 구동하기 (Testing)


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. 3. 9. 17:47 Languages

Underscore.js 프레임워크를 보면 소스에 대해 왼쪽에는 주석을 오른쪽은 소스를 보여주며 설명을 한다. 이러한 문서를 자동으로 만들어 주는 툴이 Groc 이다. 또한 루트에서 수행을 하면 하위의 모든 소스에 대한 문서화를 자동으로 만들어 주고, GitHub과 통합할 수 있다 


1. Groc 설치하기 

  - 주석을 Markdown 형태로 작성한다

  - 마크다운 포멧은 배우기 쉽고 GitHub에서도 사용하므로 반드시 익혀두자 

  - GitHub의 페이지 publishing 과 통합된다 : GitHub에서 OSS(Open Source Software) 개발한 것을 공짜로 웹 서비싱 해준다

  - 검색 테이블을 제공한다 



2. 설치하기 

  - npm (Node Package Manager)가 설치되어 있어야 한다 

sudo npm install groc -g

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

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

... 중략 ...

groc@0.3.2 /usr/local/lib/node_modules/groc

├── colors@0.6.0-1

├── spate@0.1.0

├── underscore@1.4.4

├── coffee-script@1.6.1

├── autorequire@0.3.4

├── showdown@0.3.1

├── optimist@0.3.5 (wordwrap@0.0.2)

├── jade@0.28.2 (commander@0.6.1, mkdirp@0.3.5)

├── uglify-js@2.2.5 (source-map@0.1.9)

├── glob@3.1.21 (inherits@1.0.0, graceful-fs@1.2.0, minimatch@0.2.11)

└── fs-tools@0.2.9 (async@0.2.6, lodash@1.0.1)


// syntax highlighting module pygments 설치하기

$ sudo easy_install Pygments

Best match: Pygments 1.6

Downloading http://pypi.python.org/packages/2.7/P/Pygments/Pygments-1.6-py2.7.egg#md5=1e1e52b1e434502682aab08938163034

... 중략 ...

Installing pygmentize script to /usr/local/bin   <== groc가 사용함



3. 문서만들기 

  - * 사용할 수 있다

  - 여러 옵션을 적용할 수 있다

  - CLI 명령으로 수행한다 : groc [js명칭]

  - github에서 groc를 다운받아서 직접 만들어 보자 

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

// groc 소스 받아 문서 생성하기 

$ git clone https://github.com/nevir/groc.git

Cloning into 'groc'...

remote: Counting objects: 1215, done.

...중 략 ...


groc index.js

publish_to_github null https://github.com/nevir/groc origin

  Generating documentation...

✓ /Users/git-repositories/groc/.git/groc-tmp/languages.html

✓ /Users/git-repositories/groc/.git/groc-tmp/project.html

..중략..

✓ /Users/git-repositories/groc/.git/groc-tmp/styles/default/docPage.html

✓ Documentation generated


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

// 디렉토리별로 .js 또는 .coffee 파일을 .html 파일로 문서화 한다

// git clone 후 

$ cd groc 디렉토리 이동후 모습 

// groc 를 수행한 후의 디렉토리 : lib 디렉토리에 있는 폴더를 상위 폴더로 올린다



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

// static 웹서버 구동 (참조)

$ static

serving "." at http://127.0.0.1:8080

17:35:09 [200]: /

17:35:09 [200]: /assets/style.css

17:35:09 [200]: /assets/behavior.js

17:35:09 [404]: /favicon.ico


  - 문서보기 : http://localhost:8080  Groc의 홈페이지 문서가 로컬에 만들어 졌다!


4. 사용 아이디어

  - GitHub에서 소스를 개발한다면 GitHub Page와 통합하여 API를 웹서비스 형태로 배포한다

  - 개발 소스에 대한 Code Review시에 Groc 문서로 만들어서 소스를 리뷰한다 

  


<참조>

  - Outsider dev 의 groc 소개

  - Pygments 설치하기

'Languages' 카테고리의 다른 글

APM 설치하기  (0) 2012.11.27
posted by 윤영식
2013. 2. 28. 11:29 Git, GitHub/Git Lec02

rebase를(참조) 통하여 다른 브랜치의 전체 commit 내역을 복사해오지 않고 특정 commit 내역만을 가져오고 싶을 경우 cherry-pick을 사용한다


1) 언제 사용

  - 토픽이나 패치 브랜치에서 개발된 특정 commit만을 가져오고 싶을 경우 

  - 즉, 하나의 commit만 rebase 하는 것이다 



2) 실습

  - cherry-pick을 하기전 상태

  

  [5.26 master브랜치와 별도의 토픽브랜치 ruby_client로 두개의 커밋이 존재]


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

// ruby_cleint별도 브랜치 만들어진 이후

// master 브랜치 커밋내역

git log --pretty=oneline -since="2 hours"

fatal: unrecognized argument: -since=2 hours

[nulpulum:~/git-repositories/pro_git]git log --pretty=oneline --since="2 hours"

514281f305bd001776ca41efebccf8276a6bfd98 modify dowon.js


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

// ruby_client 브랜치로 이동

$ git checkout ruby_client

Switched to branch 'ruby_client'

$ git log --pretty=oneline --since="2 hours"

0f79ccda8c4086d57a4b9ed53c87ecaf11e52ba5 modify topci.txt

40396ae5ab8a964704e0ab84809bb499f9c996b2 add topic.txt


  - check-pick 수행하기 

    + 그림에서 e43a6에 해당하는 "40396"에 대해서 master 브랜치에서 check-pick 한다

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

// master 브랜치로 토픽브랜치의 특정

// commit 내역만을 rebase = cherry-pick

$ git checkout master

Switched to branch 'master'

Your branch is ahead of 'origin/master' by 1 commit.

  (use "git push" to publish your local commits)

[nulpulum:~/git-repositories/pro_git]git branch

* master

  ruby_client


$ git cherry-pick 40396ae5ab8a964704e0ab84809bb499f9c996b2

[master 4963fef] add topic.txt

 1 file changed, 1 insertion(+)

 create mode 100644 topic.txt


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

// ruby_client의 커밋이 master 브랜치로 

// 새로운 commit을 제일 앞에 놓았다 

$ git log --pretty=oneline --since="2 hours"

4963fef54f1d6760ec4e5ff15c9684e0f5e6ad4e add topic.txt

514281f305bd001776ca41efebccf8276a6bfd98 modify dowon.js

[nulpulum:~/git-repositories/pro_git]git branch

* master

  ruby_client


  

  [5.27 e43a6 커밋내역 하나만 master의 제일 앞으로 새로운 commit이 생성]



<참조>

  - Pro Git : p127

posted by 윤영식
2013. 2. 23. 16:53 MongoDB/MapReduce

GridFS에 대하여 샘플을 돌려보자


1) 예제 다운로드 

  - git 복제한다

[~/development/mongodb]git clone https://github.com/jamescarr/nodejs-mongodb-streaming.git gridfs_mongoose

Cloning into 'gridfs_mongoose'...

remote: Counting objects: 44, done.

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

remote: Total 44 (delta 12), reused 40 (delta 8)

Unpacking objects: 100% (44/44), done.


  - 파일을 JetBrains WebStorm에서 열어보았다 

    + coffee-script 이용하여 app.js 를 app.coffee 로 작성

  


  - 실행하기전 express, mongoose, request, jade 모듈 설치

    + npm install express

    + npm install mongoose

    + npm install request


  - 실행 : http://localhost:3000 호출

    + 호출하기전 MongoDB를 start 해 놓아야 한다 

[~/development/mongodb/gridfs_mongoose]coffee app

Server running. Navigate to localhost:3000

  

    + 브라우져 호출하면 파일 업로드 화면이 나온다

  

 


2) GridFS 파일 업로드 하기 

  - 화면에서 Name을 입력하고 File 을 선택한 후 "제출" 버튼을 클릭한다 


  - MongoDB의 mongo에서 확인을 한다 : dowonFile 은 프로그램안에서 사용한 Collection 명칭이다

[~/mongodb]mongo

MongoDB shell version: 2.2.3

connecting to: test


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

//  제출전 상태

> show dbs

local (empty)


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

//  제출후 상태

> show dbs

dowonFile 0.203125GB

local (empty)

> use dowonFile

switched to db dowonFile

> show collections

applications

fs.chunks

fs.files

system.indexes

> db.applications.find();

{ "name" : "dowonGridFileSample", "_id" : ObjectId("51392f28dfa5f69404000001"), "files" : [ { "md5" : "6dda9a5cd37e113b246fd00604bf3638", "uploadDate" : ISODate("2013-03-08T00:22:01.077Z"), "chunkSize" : 262144, "length" : 7772701, "contentType" : "binary/octet-stream", "_id" : ObjectId("51392f28dfa5f69404000002") } ] }



3) 예제 분석하기 

  - *.coffee 를 컴파일하여 분석할 수도 있다 : coffee -c *.coffee 수행하면 *.js 파일 생성됨

  - 가급적 coffee-script에 익숙해 지도록 하자 (참조)

  - app.coffee 

    + 기본 환경 셋업

    + 스키마 생성

    + 호출을 위한 get/post 구성 : /new/:id 값을 RESTful 로 호출하면 파일을 다운로드 받을 수 있다 (id는 몽고디비에서 _id 참조)

  

  - gridfs.coffee

    + gridfs 전용 put, find 관련 메소드를 정의하여 모듈화 한다 


4) Mocha 테스트 

  - git clone 받은 디렉토리에서 Mocha 테스트를 수행한다 : current 디렉토리 밑으로 test 디렉토리의 테스트 파일을 자동 수행한다

  - 테스트 파일 형식

    + .js : mocha

    + .coffee : mocha --compilers coffee:coffee-script



<참조>

  - 원문 : nodejs-mongodb-streaming (Node.js mongoose+GridFS 예제)


'MongoDB > MapReduce' 카테고리의 다른 글

[MongoDB] Aggregation Framework 실습하기  (0) 2013.08.03
[MongoDB] Aggregation Framework 이해하기  (0) 2013.08.03
[MongoDB] GridFS 개념잡기  (0) 2013.02.23
posted by 윤영식
2013. 2. 23. 15:50 MongoDB/MapReduce

몽고디비가 샤딩으로 저장한 데이터들은 어떻게 조회되어 가져올 수 있을까? GridFS에 대해서 알아보자. 텐젠의 설명을 잠시 보자

GridFS is a specification for storing and retrieving files that exceed the BSON-document size limit of 16MB.

(GridFS는 16MB 넘는 사이즈의 데이터를 저장하고 조회하는 명세이다. 모든 파일을 도큐먼트로 다룬다는게 중요하겠다)


Instead of storing a file in an single document, GridFS divides a file into parts, or chunks, [1] and stores each of those chunks as a separate document. By default GridFS limits chunk size to 256k. GridFS uses two collections to store files. One collection stores the file chunks, and the other stores file metadata.

(한개의 파일로 저장하는 대신에 부분이나 청크로 나누어 분리된 도큐먼트를 청크로 저장한다. 청크사이즈는 기본 256k로 제한되어 있다. 파일 청크와 메타데이터를 저장하는 두개의 컬렉션을 사용한다)



1) 개념

  - 분산 파일 시스템 : Redis (Map-key:value- Data)

  - Redis와 같은 기능을 MongoDB에서는 GridFS에서 담당한다

  - mongoose ODM 드라이버를 통해서 GridFS를 사용할 수 있다


2) 사용하기 

  - javascript.pdf 50.3MBytes 자리 파일을 GridFS로 구성

  - 미디어 파일도 MongoDB에 컬렉션으로 관리하게 되는 것이다 

[~/mongodb]mongofiles -d dowon put javascript.pdf

connected to: 127.0.0.1

added file: { _id: ObjectId('5128625889a440df3f1359db'), filename: "javascript.pdf", chunkSize: 262144, uploadDate: new Date(1361601113683), md5: "57b836baabd09c486778e192fa6c350e", length: 53872431 }

done!


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

// 결과 

[~/mongodb]mongo

MongoDB shell version: 2.2.3

connecting to: test

> show dbs

dowon 0.203125GB

dowonDB 0.203125GB

local (empty)

> use dowon

switched to db dowon

> show collections

fs.chunks

fs.files

system.indexes

> db.fs.files.find();

{ "_id" : ObjectId("5128625889a440df3f1359db"), "filename" : "javascript.pdf", "chunkSize" : 262144, "uploadDate" : ISODate("2013-02-23T06:31:53.683Z"), "md5" : "57b836baabd09c486778e192fa6c350e", "length" : 53872431 }


  - 대용량 데이터를 그림처럼 GridFS로 저장하면

    + 맵 : 분산된 데이터를 Key=Value (map) 기반으로 찾고

    + 리듀스 : 자바스크립트 펑션을 통하여 통합한다(비즈니스 로직 구현)

  



<참조>

  - 몽구스(mongoose)를 이용하여 gridfs 구현하기 

  - 텐젠 메뉴얼 : http://docs.mongodb.org/manual/applications/gridfs/


'MongoDB > MapReduce' 카테고리의 다른 글

[MongoDB] Aggregation Framework 실습하기  (0) 2013.08.03
[MongoDB] Aggregation Framework 이해하기  (0) 2013.08.03
[MongoDB] GridFS 사용하기  (0) 2013.02.23
posted by 윤영식
2013. 2. 20. 17:36 Middleware, Cloud/Linux

로그 백업을 위하여 crontab에 등록할 Bash Shell를 작업하려 한다. 익혀간 과정을 적어보자 


1) Backup 가정

  - 3일전 로그를 압축한다.

  - 여러개의 로그파일이 다양한 위치에 존재한다

  - 압축된 로그파일은 년/월 별도 디렉토리로 이동하여 관리한다

  - 작은 업무 시스템별로 로그의 위치가 틀리다 (단, 작은 업무 시스템안에 디렉토리는 고정임)


2) 3일전 로그찾기 

  - 몇일전 날짜를 찾는 문법 (참조)

####################################################

# 1) output is 3 days ago 

# backup directory name : backup_YYYY-MM in log files

# 3일전 파일을 찾는다 

YYYYMMDD=`date +%Y%m%d --date '3 days ago'`

DATE="${YYYYMMDD:0:4}-${YYYYMMDD:4:2}-${YYYYMMDD:6:2}"

BK_DIR="backup_${YYYYMMDD:0:4}-${YYYYMMDD:4:2}"

GZIP_CNT="0"


echo ""

echo "===============================[Backup Start]============================="

echo "<< Start Time: "`date '+%F  %r'`" >>"

echo "-- Backup File Date : $DATE, Backup Dir : $BK_DIR"


3) 여러개의 로그파일이 다양한 위치에 존재함

  - 지정한 디렉토리에 파일이 있으면 gzip으로 압축한다

####################################################

# 2) run gzip for log files

# ${1} : directory full path

# ${2} : file name

function mkdir_function()

{

  if [ ! -d ${1} ]; then

    echo "  make dir : ${1}"

    mkdir ${1}

  fi

}


# backup_YYYY-MM 디렉토리가 없으면 만들고 .gz  압축파일을 월별로 관리한다 

function mv_file_function()

{

  mkdir_function "${1}/$BK_DIR"


  echo "  move : ${1}/${2}.gz to ${1}/$BK_DIR"

  mv ${1}/${2}.gz ${1}/$BK_DIR/

}


# ${1} : directory full path

# ${2} : file name

# 지정된 파일만 압축 

function gzip_function() 

{

  if [ -e ${1}/${2} ]; then

    cd "${1}"    

    echo "  gzip : ${1}/${2}"


    gzip ${1}/${2}


    mv_file_function "${1}" "${2}"  


    GZIP_CNT=`expr $GZIP_CNT + 1` 

  fi

}


# for time loop gzip

# 3일 이전의 모든 파일들 

function gzip_loop_function()

{

  cd "${1}"

  for file in `find -type f -mtime +3`; do

     local FNAME="${file:2}"

     local EXT="${FNAME##*.}"

     if [ "$EXT" != "" ] && [ "$EXT" != "gz" ] && [ "$EXT" != "sh" ]; then

        gzip_function "${1}" "${FNAME}"

     fi

  done

}


4) 업무 단위 시스템별로 로그의 위치 지정 : 로그 지정하여 압축 수행함 

  echo "-- [START] ${JDOMAIN} backup : TX.xml & Error.xml log"

  gzip_function "/tmp/logs" "Transactions.log.$DATE"

  gzip_function "/tmp/logs" "Error.log.$DATE"


  # gc old log

  echo "-- [START] ${JDOMAIN} backup : old gclog log"

  gzip_loop_function "/tmp/logs/$JDOMAIN/old/gclog"

 

  # nohup old log

  echo "-- [START] ${JDOMAIN} backup : old nohup log"

  gzip_loop_function "/tmp/logs/$JDOMAIN/old/nohup"


5) 전체 Shell Script


<참조>

  - 백업받을 디렉토리를 만들어야 하는 경우 : 하기 내용은 function을 이용한 참조 내역임 (다른 유사 참조)

  - Bash Shell 문법을 익혀보자 (참조)

  - Bash Shell 문법 : 옵션 잘 설명됨 (참조)

posted by 윤영식
2013. 2. 20. 17:01 Backbone.js

프론트앤드 자바스크립트 프레임워크의 시작은 백본의 이해가 기본이라고 생각합니다. 어떻게 배워야 할지 알아보죠


1) 이럴 때 사용하자 

  - modern single page web application 개발시 사용

  - 다른 최신의 (meteor.js, derby.js, ember.js)를 시작하기 이전에 기본으로 배우자 

  - 자바스크립트 MVC or MV* 프레임워크에 대해서 잘 이해하고 쓰자

  - Node.js와 궁합 잘 맞음 


2) 백본 기초 다지기

  - Developing Backbone.js Applications 번역서로 시작하자


3) 마스터 하기 

  - 자바스크립트를 먼저 익혀야 한다

  - 자바스크립트 생기초 개념

    + JavaScript Objects in Detail

    + JavaScript Variable Scope and Hoisting Explained

    + Understand JavaScript Closure with ease : 꼭 읽자. 설명 잘 했다

  - Model, View, Router, Collections에 대한 개념 정리

    + 모델이란

    + 뷰란

    + 라우터란

    + 컬렉션이란

  - 백본사용하여 개발된 예제 Wine Cellar 예제 시작하기 : Part 1

  - Developing Backbone.js Applications 복습 ^^;

  - 백본관련 블로깅 글 2개 읽기 

    + 백본의 MV* 각 역할에 대한 이해

    + 백본 시작하기 3단계

  - Wine Cellar CRUD 이해하기 : Part 2

  - Wine Cellar 어플리케이션 성능 개선 및 배울점 : Part 3 (꼭 읽자)

  - 마무리 블로깅 꼭 읽자 

    + 뷰와 서브뷰간의 통신 방법 이해하기1, 이해하기2

    + 페이지 변경 제어 이해하기1, 이해하기2


전체 하는데 30시간 정도 투자하시라 (하루 3시간씩 2주면 되겠네요)


4) 마무리

  - 이제 Node.js 를 배워보자 

  - 백본과 노드에 대해서 개념 잡았으면 현대적인 웹앱만들 준비 끝~~~

  - 실제 구현사례는 http://dailyjs.com/web-app.html 여기서 틈틈히 살펴보자 


5) 기타

  - 핸들바 템플릿엔진에 대한 이해

  - 백본 확장, 백본 Grunt, Unit Testing 방법등을 알아본다



<참조>

  - 원문 : http://javascriptissexy.com/learn-backbone-js-completely/

posted by 윤영식