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

Publication

Category

Recent Post

2013. 4. 5. 16:27 AngularJS/Prototyping

AngularJS를 가지고 Twitter Search 기능을 프로토타입핑한다. Angular에서는 jsonp를 어떻게 다루는지 살펴보자 



1) Twitter 검색

  - 검색어를 넣고 트위터로 jsonp 검색을 하고 결과를 출력한다


  - angularJS와 angularJS resource 를 사용한다

    + 서버사이드 데이터를 RESTful하게 가져오기 위한 방법을 제공한다

    + API : http://docs.angularjs.org/api/ngResource.$resource (1.1.4 소스)


  - 사전준비 

     + 소스복제 : git clone https://github.com/ysyun/prototyping

     + 트윗소스 : cd prototyping/angularjs/twitter


  - twitter.html 소스

    + <html ng-app="Twitter"> 를 반드시 추가한다. 설정하지 않으면 하기 에러발생. twitter.js에서 모듈명으로 사용


    + twitter.js angular controller 파일을 별도로 코딩한다

    + <div ng-controller="TwitterCtrl" 추가 

    + <input ng-model="searchTerm" 프로퍼티 추가하여 controller 내의 doSearch function 안에서 검색 파라미터로 사용한다 

    + <button ng-click="doSearch()" 추가하여 검색하기 버튼 클릭하면 호출될 function지정

    + <tr ng-repeat 추가 : angular $resource를 통한 jsonp 호출 결과를 리스팅한다 

<!doctype html>

<html lang="en" ng-app="Twitter">
<head>
<meta charset="UTF-8">
<title>AngularJS Hello World Twitter</title>
<script src="http://code.angularjs.org/1.1.4/angular.min.js"></script>
<script src="http://code.angularjs.org/1.1.4/angular-resource.min.js"></script>

<script src="twitter.js"></script>
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">

</head>
<body>

<div ng-controller="TwitterCtrl">
<form class="form-horizontal">
<input type="text" ng-model="searchTerm">
<button class="btn" ng-click="doSearch()">
<i class="icon-search"></i>Search
</button>
</form>

<table class="table table-striped">
<tr ng-repeat="tweet in twitterResults.results">
<td>{{tweet.text}}</td>
</tr>
</table>
</div>

</body>
</html>


  - twitter.js 소스 

    + angular.module : twitter.html에서 <html ng-app="Twitter" 명칭을 넣어줌. $resource를 사용하기 위하여 ngResource 의존

    + $scope.twitter.get() 호출하게 되면 jsonp 방식으로 호출하여 결과값을 가져온다

angular.module('Twitter', ['ngResource']);

function TwitterCtrl($scope, $resource){
$scope.twitter = $resource('http://search.twitter.com/:action',
{action:'search.json', q:'angularjs', callback:'JSON_CALLBACK'},
{get:{method:'JSONP'}});

// $scope.twitterResults = $scope.twitter.get(function(){ alert(' hi dowon jsonp '); });

$scope.doSearch = function () {
$scope.twitterResults = $scope.twitter.get({q:$scope.searchTerm});
}

}



2) 동영상


  - 소스 : https://github.com/ysyun/prototyping/tree/master/angularjs/twitter

posted by 윤영식
2013. 4. 5. 14:36 AngularJS/Prototyping

Backbone 으로 todo 목록을 처음 만들어 보았을 때 이해와 타입핑을 하는데 많은 시간을 소비하였다. Angular를 접하면서 Todo 목록 만들기가 얼마나 쉽고 빠르게 몇분만에 개발되는지 감상해 보자. 직접 해보았는데 20분정도 소요된것 같다. 그간 배운 빅데이터는 이와 같은 쉬운 웹앱기술로 통합되어 스마트 기기에 표현될 수 있겠다. 



1) 할일 목록 만들기 

   - 사전준비 

     + 소스복제 : git clone https://github.com/ysyun/prototyping

     + 투두소스 : cd prototyping/angularjs/todo


   - 할일 목록을 등록하고 완료체크를 하고 완료된 것은 삭제하는 기능을 갖는다

   

  - Bootstrap, Underscore를 이용하고, Angular Controller를 별도 .js로 분리한다 

  - todo.html

    + <html ng-app> 추가

    + <script> : angular.js, underscore.js, todo.js, bootstrap.css 파일을 연결한다

    + <div ng-controller> 추가 : controller function은 todo.js안에 구현 

    + getTotalTodos() : controller function 안에 구현 - 목록이 변경되면 자동 호출되어 바인딩됨 

    + <li ng-repeat> 추가 : todos 목록 배열을 열거한다 

    + <form> 의 <input ng-model="formTodoText"> 속성을 정의하고 controller function에서 사용토록 한다  

    + <button ng-click="clearCompleted()" 추가 : controller function에서 구현한다

<!doctype html>
<html lang="en" ng-app>
<head>
<meta charset="UTF-8">
<title>AngularJS Hello World using Directives</title>
<script src="http://code.angularjs.org/1.1.4/angular.min.js"></script>
<script src="../../underscore/underscore-min.js"></script>
<script src="todo.js"></script>
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="todo.css">
</head>
<body>

<div ng-controller="TodoCtrl">
<h2>Total count : {{getTotalTodos()}}</h2>
<ul class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
<form class="form-horizontal">
<input type="text" ng-model="formTodoText" ng-model-instant>
<button class="btn" ng-click="addTodo()">
<i class="icon-plus"></i>Add
</button>
</form>

<button class="btn-large" ng-click="clearCompleted()">
<i class="icon-trash"></i>Clear Completed
</button>
</div>

</body>
</html>


    + 브라우져에 출력되는 DOM Element 소스 

    


  - todo.css

.done-true{
text-decoration: line-through;
color: grey;
}


  - todo.js

    + todos : 목록 배열

    + function들 정의

function TodoCtrl($scope) {

$scope.totalTodos = 4;

// 가상 데이터를 만든다
$scope.todos = [
{text:'Learn you', done:false},
{text:'build SPA', done:false}
];

// html에서 전체 건수를 return 해 준다
$scope.getTotalTodos = function() {
return $scope.todos.length;
}

// Underscore.js 를 이용하여 done=false 인 것만 배열로 뽑아낸다
$scope.clearCompleted = function() {
$scope.todos = _.filter($scope.todos, function(todo){
return !todo.done;
});
}

// todos 배열에 html에서 입력한 값을 넣어준다
$scope.addTodo = function() {
$scope.todos.push({text:$scope.formTodoText, done:false});
$scope.formTodoText = '';
}
}



2) TodoList 만들기 동영상

 

  - 소스 : https://github.com/ysyun/prototyping/tree/master/angularjs/todo

posted by 윤영식
2013. 4. 4. 17:59 AngularJS/Prototyping
AngularJS의 세계에 빠져보자.  BackboneJS 를 통하여 자유로운 영혼의 Client Side MV* framework 맛 보았다면 이제 AngularJS를 통하여 좀 더 절제되고 Fast Creating 가능한 방법에 대해 고민해 볼 때가 된 것 같다. 커뮤니티를 통하여 성숙하고 있고, 좋은 문서와 구글이라는 든든한 백그라운드까지 갖추고 있어서 무한한 성장 가능성을 가지고 있는 프레임워크라고 본다


1) Hello World
  - 구글 CDN 참조
  - 소스
<!doctype html>
<html lang="en" ng-app>
<head>
<meta charset="UTF-8">
<title>AngularJS Hello World</title>
</head>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>

<script>
function DowonController($scope) {

$scope.DowonProperty = "YoungSik";
}
</script>

<div ng-controller="DowonController">

<input type="text" ng-model="DowonProperty" ng-model-instant/>
Hello World

{{DowonProperty}}

</div>

</body>
</html>
 
  - 코딩 순서
    + angularJS CDN script 태그 추가 
    + <html ng-app> 추가
    + <div ng-controller> 추가 
    + controller function 구현 : $scope에 속성추가/설정
    + ng-model 추가 
    + {{ property }} 클라이언트 템플릿 추가 
    + 수행 : WOW!!!

  - 이해하기 
    + ng-app를 선언한 DOM 하위는 angular를 통하여 제어가 가능하다. 
    + ng-model을 HTML안에서 선언하면 controller function에서 프로퍼티를 사용할 수 있다
    + controller function에서 프로퍼티를 선언하면 HTML안에서 사용할 수 있다
    + 결국 Angular.js 는 양방향 Live Stream(Dynamic) binding을 가능하게 해준다  


2) 동영상
    


  - 소스 : https://github.com/ysyun/prototyping/blob/master/angularjs/hellworld.html



<참조> 

  - 동강 모음 http://johnlindquist.com/

  

  - 페이스북 공개그룹 : https://www.facebook.com/groups/frontiner/permalink/525021784200620/


AngularJS Hello World 
http://www.youtube.com/watch?v=uFTFsKmkQnQ&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

AngularJS - Getting Started - Google+
https://plus.google.com/communities/115368820700870330756/stream/f1fab692-830e-4341-b010-d0078e18719f

1. Devoxx 2012: AngularJS 
http://www.youtube.com/watch?v=4EVBg1pNdtc&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

2. Mastering AngularJS - 1. Introduction 
http://www.youtube.com/watch?v=DfstuptCfUo&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

3. Mastering AngularJS - 2. Using Directives .....감동의 도가니탕
http://www.youtube.com/watch?v=2SZQslQOgNg&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

4. AngularJS Tutorial .....감동의 도가니탕
http://www.youtube.com/watch?v=WuiHuZq_cg4&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

5. AngularJS Twitter Search 
http://www.youtube.com/watch?v=IRelx4-ISbs&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

6. AngularJS - Directive Tutorial 
http://www.youtube.com/watch?v=Yg-R1gchccg&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

7. AngularJS - Custom Components - Part 1 
http://www.youtube.com/watch?v=A6wq16Ow5Ec&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

8. AngularJS - Custom Components - Part 2 
http://www.youtube.com/watch?v=nKJDHnXaKTY&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

9. AngularJS - Communicating Between Controllers 
http://www.youtube.com/watch?v=1OALSkJGsRw&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

10. AngularJS and DOM Manipulation 
http://www.youtube.com/watch?v=bk-CC61zMZk&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

11. AngularJS - Handy Tricks With Developer Tools ...어려워 나중에
http://www.youtube.com/watch?v=4TM6wG9UW7s&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

12. AngularJS Routing ...............................................................내일은 여기서부터
http://www.youtube.com/watch?v=5uhZCc0j9RY&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

13. AngularJS - Behave like Gmail using Routes with Resolve 
http://www.youtube.com/watch?v=P6KITGRQujQ&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

14. AngularJS + Chosen Plugin = Awesome 
http://www.youtube.com/watch?v=8ozyXwLzFYs&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

posted by 윤영식
2013. 4. 3. 14:15 Testing, TDD/Test First

Node.js를 사용하기로 하면서 TDD를 위하여 Mocha를 선택하였다. 그리고 클라이언트단의 테스트 또한 Mocha로 이용해 보려 하였으나 DOM 조작이 많을 경우와 테스트 간결함등등에 강점이 많은 QUnit이 오히려 매력적으로 다가 왔다



1) QUnit vs Mocha

  - QUnit

    + DOM 조작관련 Client 단위 테스트에 적합

    + assert.equal이 아니라 equal만 사용하면 됨 

    + UI에서 테스트 모듈별로 콤보 선택하여 볼 수 있음

    + 클라이언트단 라이브러리들에서 주로 사용함 (BackboneJS 등)

    + PhantomJS와 연계하여 테스트

   



  - Mocha

    + 다양한 라이브러리를 접목 할 수 있다

    + 브라우져 UI 와 소스보기등이 QUnit 보다 나아 보임

    + equal 이 아니라 assert.equal을 씀 (assert의 경우) 즉, DRY 위배

    + UI에서 테스트 결과 전체가 리스팅 되고 선택하여 볼 수 없음 

 


** Backbone의 QUnit  코드를 Mocha를 가지고 메소드별로 TDD방식으로 테스트 해보았다. 라이브러리 테스트 코드를 하나하나 체크하면 전체 API의 동작방식을 심도 있게 이해 할 수 있다


https://github.com/ysyun/prototyping/tree/master/tdd/backbone-mocha/tests



2) 결론

  - 클라이언트 : QUnit + PhantomJS(CasperJS) 를 사용하여 테스트 진행

  - 서버 : Mocha + Assert 을 사용



<참조>

  - 원문 : Test  Libraries 비교 - 테스트 라이브러리별 사용처 권고함

posted by 윤영식

에릭리스가 이야기하는 린 스타트업은 이미 예전부터 있어 왔다. 그리고 나에게 현재 진행형이다. 

 Do it, Measure, Learn


  - 프로그래머는 현대판 락스타다. 컴퓨터 석학이 되려는게 아니다 그냥 이웃에서 도움이 되는 재미있는 뭔가를 하고 싶었다

    


  - 작은 것부터 시작하라. 린 스타트업의 MVP (Minimum Viable Product) 최소 요건 제품을 시장에 내놓고 평가받고 배워라

    

  - 다른 사람이 이용할 수 있는 당신만의 뭔가를 만들 수 있다. 그 진실을 깨달으면 당신의 삶은 영원히 바뀔 것이다
    


posted by 윤영식
2013. 4. 1. 19:41 Git, GitHub/GitHub

낚시성 제목이지만 한번쯤 링크를 걸어 놓고 궁금한 사항 발생시 첫번째로 확인해 볼 레퍼런스 사이트를 정리해 본다 



Git 메뉴얼

  - git-scm.com 의 한글 번역 메뉴얼

 




명령어 레퍼런스

  - gitref.org : 명령어에 대한 예와 다이어그램이 잘 나와있음






Merge 와 Diff Tool 바꾸기 

  - git의 기본적인 merge, diff 툴을 좀 더 괜찮은 넘으로 바꾸어 사용하기

  - p4merge 툴로 바꾸기 : 다운로드 받아서 설치한다

  - extMerge 파일 생성

/Users/nulpulum> cd /usr/local/bin

/usr/local/bin> cat extMerge

#!/bin/sh

/Applications/p4merge.app/Contents/MacOS/p4merge $*


  - extDiff 파일 생성

/usr/local/bin> cat extDiff

#!/bin/sh

[ $# -eq 7 ] && /usr/local/bin/extMerge "$2" "$5"


  - 환경설정 명령 : git config --global <옵션>

// 옵션들 

merge.tool=extMerge

mergetool.extMerge.cmd=extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"

mergetool.extMerge.trustexitcode=false

diff.external=extDiff


  - ~/.gitconfig 내역 변경

[merge]

tool = extMerge

[mergetool "extMerge"]

cmd = extMerge \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"

[mergetool] <-- 제거

trustExitCode = false

[diff]

external = extDiff


  - 사용법은 p4merge 툴로 바꾸기 참조하시라





기초 문법 다시 보자 





Git 원리 이해하기 


posted by 윤영식
2013. 4. 1. 11:20 Middleware, Cloud/WAS

JVM에서 Thread Dump는 현재 수행중인 쓰레드에 대한 호출경로 StackTrace를 보기 위한 것이고 Heap Dump는 현재 Heap에서 점유되고 있는 객체들에 대한 조사를 위하여 필요한 내역이다 



1. Thread Dump 수행


  - 명령 : kill -3 <pid> 

  - <pid> 는 JVM의 process ID 이다 

  - Thread Dump 내역을 통하여 Thread ID와 호출에 대한 추적을 할 수 있다 

Full Java thread dump with locks info

"ZipFile Lock Reaper" Id=1826 in TIMED_WAITING on lock=java.util.TaskQueue@76a7850e

at java.lang.Object.wait(Native Method)

at java.util.TimerThread.mainLoop(Timer.java:509)

at java.util.TimerThread.run(Timer.java:462)


Locked synchronizers: count = 0


"ajp-0.0.0.0-8009-20" Id=1323 in WAITING on lock=org.apache.tomcat.util.net.AprEndpoint$Worker@12a2bc0f

at java.lang.Object.wait(Native Method)

at java.lang.Object.wait(Object.java:485)

at org.apache.tomcat.util.net.AprEndpoint$Worker.await(AprEndpoint.java:1989)

at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:2014)

at java.lang.Thread.run(Thread.java:662)


Locked synchronizers: count = 0


"ajp-0.0.0.0-8009-19" Id=1321 in RUNNABLE (running in native)

at org.apache.tomcat.jni.Socket.recvbb(Native Method)

at org.apache.coyote.ajp.AjpAprProcessor.readt(AjpAprProcessor.java:1072)

at org.apache.coyote.ajp.AjpAprProcessor.readMessage(AjpAprProcessor.java:1163)

at org.apache.coyote.ajp.AjpAprProcessor.process(AjpAprProcessor.java:368)

at org.apache.coyote.ajp.AjpAprProtocol$AjpConnectionHandler.process(AjpAprProtocol.java:425)

at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:2036)

at java.lang.Thread.run(Thread.java:662)

... 중략 ..




2. Heap Dump 수행


  - JVM GC 블로깅에서 Old영역을 많이 사용할 경우 일반적으로 사용되지 않는 객체들이 Reference되어 GC되지 않고 남아 있을 가능성이 높다. 이때는 어떤 객체들이 많이 점유되고 있는지 조사할 필요가 있다

  - 즉, Old 영역을 많이 점유하고 있으면 Full GC가 자주 오래동안 발생할 수 있으므로 업무 응답시간에 문제 또는 장애를 일으킬 소지가 있다

  - JDK6 명령 : jmap -dump:format=b,file=<fileName> <PID>    

  - jmap은 JDK_HOME/bin 에 포함되어 있음




3. Heap Dump 분석

 

  - Eclipse Memory Analyzer Tool (mat) 을 이용한다

  - 독립 프로그램 다운로드 : Heap Dump Size는 몇 Gbytes 씩 되므로 64bit OS 에서 64bit mat 를 다운로드 받는다

  - MemoryAnalyzerTool.ini 파일의 초기값 설정을 Heap Dump 파일 사이즈에 따라 적절히 한다

    + 내 경우 : -Xms2048m -Xmx8192m (Heap Dump file size 6Gbytes 일때) 

-XX:MaxPermSize=256m

-Xms40m

-Xmx512m


  - mat에서 Heap Dump 파일을 로딩한다. 이때 파일 확장자는 .hprof 여야 한다. 

    6Gbytes 로딩에 대략 수시간이 소요되니 인내심이 필요 하다  

  - mat 통하여 Heap Dump 분석할 때 로컬 메모리가 부족할 경우 Swap Size를 확장하여 준다 (스왑 설정하기)



<참조>

  - OOM 발생에 대한 고찰

  - Heap Dump 생성하기

  - Eclipse Memory Analyzer Tool Download

  - JHat 분석툴, JVisualVM 분석툴 (jdk1.6제공)

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

EmberJS를 익히는 방법에 대한 개념을 잡아보자



1) 개념잡기 

  - GitHub 메뉴얼 보기 

    + Binding : 프로퍼티 명칭뒤에 Binding을 붙여주면 변경에 대하여 자동 반영된다 

    + 계산된 속성 : 함수를 속성으로 다룰 수 있다 

    + 자동 업데이트 템플릿 : 자동 렌더링 in View


  - 홈페이지의 Guide 보기 

  - 가장 간단한 코드 


2) 기타 튜토리얼

  - ember.js 실습1


  - ember.js 실습2


  - ember.js 실습3



posted by 윤영식
2013. 3. 30. 12:33 Languages/CoffeeScript

CoffeeScript의 컴파일과 Minify, 하나의 파일로 만들기 그리고 자동 변경체크하여 컴파일 하기를 좀 더 쉽게 하는 Coffeebar를 알아보자 



1) Coffeebar 기능과 설치

  - 기능

Supports Literate CoffeeScript

Robust file watching

Cross-platform

Minification

Concatenation of multiple source files

Shows the actual source line when concatenated files have a compile error


  - 설치

$ sudo npm install -g coffeebar

/usr/local/bin/coffeebar -> /usr/local/lib/node_modules/coffeebar/bin/coffeebar

coffeebar@0.2.0 /usr/local/lib/node_modules/coffeebar

├── mkdirp@0.3.5

├── xcolor@0.1.0

├── coffee-script@1.6.2

├── commander@1.1.1 (keypress@0.1.0)

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

├── beholder@0.1.2 (async@0.2.6, minimatch@0.2.11)

└── uglify-js@2.2.5 (optimist@0.3.5, source-map@0.1.19)



2) CLI 사용법

  - 옵션들

Usage: coffeebar [options] [path ...]


Options:


  -h, --help           output usage information

  -V, --version        output the version number

  -b, --bare           compile without a top-level function wrapper

  -m, --minify         minify output files

  -o, --output <path>  output path

  -s, --silent         suppress console output

  -w, --watch          watch files for changes


  - 컴파일 하기

$ coffeebar test.coffee


  - 소스 디렉토리(src)를 지정하여 컴파일 디렉토리(lib) 지정하기 

$ coffeebar src -o lib


  - 소스 디렉토리(src)를 지정하여 하나의 파일로 합치기

$ coffeebar src -o joined.js


  - 소스 디렉토리(src)를 지정하여 컴파일 디렉토리(lib) 지정하고 .coffee 소스 파일 변경되면 자동 컴파일하기 

$ coffeebar src -o lib -w



3) API 사용법

  - coffeebar(inputPaths, [options]) : inputPaths에 소스 디렉토리 지정하면 options에 따라 수행을 결정한다 

  - 옵션들

bare - (boolean) CoffeeScript compiler option which omits the top-level function wrapper if set to true.

minify - (boolean) Minify output files. Defaults to false.

output - (string) The path to the output file. If the path has a file extension, all files will be joined at that location. Otherwise, the path is assumed to be a directory.

silent - (boolean) Suppress all console output. Defaults to true.

watch - (boolean) Watch all files and directories for changes and recompile automatically. Defaults to false.


  - 예 : output 위치와 합칠 파일을 지정

// use the api in source

var coffeebar = require('coffeebar');

coffeebar('src', {output: 'lib/app.js'});


$ npm install

$ npm test



<참조>

  - 원문 : nmpjs.org coffeebar

posted by 윤영식
2013. 3. 27. 18:04 Dev Environment/Mac, Dev Tools

Chrome 의 Dev Tools에 Backbone/Ember/AngularJS 프레임워크 및 CoffeeScript Console을 확장해 본다. 

최종 확장한 모습이다 



1) CoffeeConsole 

  - 로컬에 파일 복제 : git clone https://github.com/snookca/CoffeeConsole.git

  - CoffeeConsole 폴더 밑에 coffeeconsole.crx 파일 존재

  - 크롬브라우져 URL 입력창에 chrome://extensions/  호출

  - coffeeconsole.crx 파일을 "확장프로그램 설치"창으로 drag&drop 하고 팝업창뜨면 설치 OK

  - 결과 확인 (크롬 : command+option+i)

  

  좌측에서 커피코드를 짜면 우측에서 실시간 해석되어 자바스크립트 코드가 보인다



2) BackboneJS

  - 로컬에 파일 복제 : git clone https://github.com/spect88/backbone-devtools.git

  - 크롬브라우져 URL 입력창에 chrome://extensions/  호출

  - "압축해제된 확장 프로그램 로드.." 버튼 클릭 -> backbone-devtools 폴더 선택하고 OK

  - [] Inject Backbone.Debug 를 선택하면 DevTools을 띄운 페이지가 reload된다 

  

  ** 결정적 단점은 AMD가 아직 지원이 안된다 ㅠ.ㅠ; 그냥 <script> 태그 통해서 BackboneJS 로딩해야 함

      window.Backbone available on DOMContentLoaded (no require.js support as of now, sorry)

  ** QUnit 이나 Mocha를 통하여 TDD 할 때 사용하면 된다. 



3) EmberJS

  - 로컬에 파일 복제 : git clone https://github.com/tildeio/ember-extension.git

  - 크롬브라우져 URL 입력창에 chrome://flags/ 호출하고 "실험실 확장 프로그램 API" 사용을 해야한다 

  - 크롬브라우져 URL 입력창에 chrome://extensions/  호출

  - "압축해제된 확장 프로그램 로드.." 버튼 클릭 -> backbone-devtools 폴더 선택하고 OK

  - Ember가 추가되었다  

  


4) AngularJS

  - 로컬에 파일 복제 : git clone https://github.com/angular/angularjs-batarang.git

  - 크롬브라우져 URL 입력창에 chrome://extensions/  호출

  - "압축해제된 확장 프로그램 로드.." 버튼 클릭 -> backbone-devtools 폴더 선택하고 OK

  - AngularJS 설치 화면 

  


Build Tool인 Grunt 또한 확장을 할 수가 있다. Grunt와 함께 확장툴은 사용하면서 다시 블로깅하기로 한다. 



<참조>

  - 원문 : DevTools Extensions in Chrome for Developers

posted by 윤영식
2013. 3. 26. 22:29 Dev Environment/Mac, Dev Tools

크롬을 사용하면서 개발이나 디자인시에 유용한 Extension을 설치하여 사용하자



1) 개발 및 디자인시 꼭 설치해야할 것들 

  - 25 가지 유용한 크롬용 익스텐션 툴들

    + 창 사이즈를 다양하게 띄워서 Responsive Web 테스트

    + Ruler를 통한 사이즈 크기 측정

    + FireBug lite for Chrome : 버그 있어서 제거함

    + Speed Tracer

    + Trello 는 Scrum 도구로 사용하고 있음 : PC, Android, iPhone, iPad 지원하는 서비스

    + getPocket 은 현재 보고 있는 화면 저장 및 태깅 : PC, Android, iPhone, iPad 지원 서비스

   



2) 크롬 Inspector의 바탕 스킨 바꾸기

  - 크롬 바탕색을 검은색으로 변경, 여러 스킨들 소개

  - chrome-devtools://devtools/devTools.css  호출하면 기본 css가 나옴

  - https://gist.github.com/bentruyman/1150520   의 Custom.css 파일로 대체한다 

    + Mac: ~/Library/Application Support/Google/Chrome/Default/User StyleSheets/Custom.css

    + PC: C:UsersYourUsernameAppDataLocalGoogleChromeUser DataDefaultUser StyleSheetsCustom.css


  - 검은색으로 나와서 좀 더 가독성이 높아진다  

    

    

** 현재 쓰고있는 것은 RubyBlue가 가장 가독성이 좋은 것 같다. 개인적 느낌으로...^^ (Custom.css.rubyblue.dowon)

Custom.css.zip

Custom.css.rubyblue.dowon



3) Mac에서 크롬 short key list

  - 크롬 hot keys

posted by 윤영식
2013. 3. 26. 18:29 Backbone.js

마리오네트는 엔터프라이즈 애플리케이션 개발시 필수적으로 갖추어야할 부분에 대한 고려사항이 반영되어 있으며 백본을 기본으로 확장하여 기능을 추가하였다. 마리오네트 공연을 보자



1) 마리오네트 장점

  - 모듈, 이벤트 지향 아키텍쳐의 확장

  - View에 대한 rendering 반복코드 줄여줌 

  - Region and Layout 개념을 통한 화면 통합

  - 메모리 관리 및 좀비 화면면/리젼/레이아웃등 관리

  - EventBinder 통한 이벤트 누수방지 (clean-up)

  - EventAggreagator 통한 이벤트 지향 아키텍쳐

  - 필요한 부분만 선택적으로 적용 가능



2) 반복 렌더링 코드

  - 백본 + 언더스코어 코드

    + View 에 대한 일반적인 define -> build -> render -> display == boilerplate code 라고 한다 

    + 이러한 코드가 애플리케이션 내에서 계속해서 반복된다 

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

// template file

<script type="text/html" id="my-view-template">

  <div class="row">

    <label>Name:</label>

    <span><%= name %></span>

  </div>

</script>


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

// 백본 View 코드 

var MyView = Backbone.View.extend({

  template: $('#my-view-template').html(),


  render: function(){

    // compile the Underscore.js template

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


    // render the template with the model data

    var data = this.model.toJSON();

    var html = compiledTemplate(data);


    // populate the view with the rendered html

    this.$el.html(html);

  }

});


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

// 백본 App

var Dowon = new Person({

  name: 'Hi YoungSik'

});

var myView = new MyView({

  model: Dowon

});

myView.render();


//템플릿 결과 html 내역을 include

$('#content').html(myView.el)


  - Marionette의 ItemView 사용

    + render 메소드를 내장하고 있다. 즉 render 메소드가 제거되었다

    + Underscore를 기본 사용한다 

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

// 마리오네트 View 코드 

var MyView = Marionette.ItemView.extend({

  template: '#my-view-template'

});



3) 뷰의 Render 및 참조 오류 제거

  - View 인스턴스와 Event 핸들러를 메모리 관리 걱정없이 쉽게 다루도록 해준다 

  - 백본 코드

    + 하나의 뷰를 만들고 내부적으로 이벤트 핸들러를 등록한다 

    + 뷰의 변수에 두개의 레퍼런스가 할당되면 첫번째 뷰 객체는 좀비가 되지만 이벤트 핸들러가 2회 반응한다

       즉, View 의 render가 두번 호출되어 alert이 두번 뜸

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

// 좀비 View 만들기 

var ZombieView = Backbone.View.extend({

  template: '#my-view-template',

  initialize: function(){

    // bind the model change to re-render this view

    this.model.on('change', this.render, this);

  },

  render: function(){

    // This alert is going to demonstrate a problem

    alert('We`re rendering the view');

  }

}); 


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

// 수행

var Person = Backbone.Model.extend({

  defaults: {

    "name": "hi dowon"

  }

});


var Dowon = new Person({

  name: 'youngsik'

});


// create the first view instance

var zombieView = new ZombieView({

  model: Person

});


// 해결을 위하여 zombieView.stopListening(); 호출이 필요한다


// create a second view instance, re-using

// the same variable name to store it

zombieView = new ZombieView({

  model: Person

});

// set 호출하여 이벤트 trigger 발생

Person.set('name', 'yun youngsik');


  - Marionette 코드 

    + listenTo를 사용하면 중복된 이벤트 핸들러는 제거함 

var ZombieView = Marionette.ItemView.extend({

  template: '#my-view-template',

  initialize: function(){

    // bind the model change to re-render this view

    this.listenTo(this.model, 'change', this.render, this);

  },


  render: function(){

    // This alert is going to demonstrate a problem

    alert('We`re rendering the view');


  }

});



4) Region을 통한 View lifeCycle 자동 관리

  - 템플릿을 해석하고 데이터 맵핑후 jQuery를 이용하여 html()이용하여 템플릿 삽입하는 부분도 boilerplate 코드이다 

    위 예에서 $('#content').html(myView.el); 코드

  - View 메모리 문제처럼 화면에 보이는 부분을 Region 으로 사용하면 개별 View들에 대한 LifeCycle 을 관리해 준다 

  - DOM element를 관리하는 Region 을 생성하고 View의 관리를 Region에게 위탁한다 

    + el 을 지정한다

    + render의 구현은 필요없다

    + view에 대한 stopListening를 호출할 필요가 없고 자동으로 DOM에서 이전 view는 제거된 후 새로운 view가 render 된다다

// create a region instance, telling it which DOM element to manage

var myRegion = new Marionette.Region({

  el: '#content'

});


// show a view in the region

var view1 = new MyView({ /* ... */ });

myRegion.show(view1);


// somewhere else in the code,

// show a different view

var view2 = new MyView({ /* ... */ });

myRegion.show(view2); 



5) Application을 통한 Region 통합 관리

  - Marionette ToDoMVC AMD방식의 GitHub 소스를 통하여 살펴보자 (참조 문서에 없는 자체 분석 결과임)

    + git clone -b marionette https://github.com/derickbailey/todomvc.git  수행하여 clone 할 때 marionette 브랜치로 구성한다

       git clone https://github.com/derickbailey/todomvc.git  후에 git checkout marionette 해도 된다 

    + cd todomvc 디렉토리로 들어가서 static 수행한다 (8080 port)

    + 브라우져에서 바로 호출한다

       http://localhost:8080/labs/dependency-examples/backbone_marionette_require/index.html

    


  - backbone_marionette_require 의 index.html 의 body 태그 중요내역

    + todoapp 섹션안에 #header, #main, #footer selector가 존재

    + Require.js 를 통한 js 폴더 밑의 main.built.js 파일을 수행(Minified 파일) -> main.js 파일 수행함 

<body>

<section id="todoapp">

  <header id="header">

  </header>

  <section id="main">

  </section>

  <footer id="footer">

  </footer>

</section>

<footer id="info">

  <p>Double-click to edit a todo</p>

  <p>Created by <a href="http://github.com/jsoverson">Jarrod Overson</a></p>

  <p><a href="./index-dev.html">View the unbuilt version</a></p>

</footer>

<script src="../../../assets/base.js"></script>

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

...

</body> 


  - main.js 파일에서 AMD 모듈 환경설정

    + 마리오네트를 AMD 모듈에 넣는다. 최근 버전은 AMD버전이 있으므로 shim에 설정하지 않아도 됨

    + app.js 파일을 수행한다 

    + baseUrl 을 설정하지 않았으므로 현재 디렉토리가 된다 

require.config({

  paths : {

    underscore : 'lib/underscore',

    backbone   : 'lib/backbone',

    marionette : 'lib/backbone.marionette',

    jquery     : '../../../../assets/jquery.min',

    tpl        : 'lib/tpl'   // underscore micro template 

  },

  shim : {

    'lib/backbone-localStorage' : ['backbone'],

    underscore : {

      exports : '_'

    },

    backbone : {

      exports : 'Backbone',

      deps : ['jquery','underscore']

    },

    marionette : {

      exports : 'Backbone.Marionette',

      deps : ['backbone']

    }

  },

  deps : ['jquery','underscore']

});


require(['app','backbone','routers/index','controllers/index'],function(app,Backbone,Router,Controller){

  "use strict";


  // 애플리케이션 시작

  app.start();


  // 라우터에 컨트롤러를 설정함

  new Router({

    controller : Controller

  });


  // 백본 이력 시작

  Backbone.history.start();

});

 

  - app.js 에서 Application.addRegions를 통하여 화면 Fragment를 등록한다

    + 애플리케이션이 초기화 된후 Fragment의 히스토리를 시작한다 (참조)

    + collections/TodoList.js 파일은 Backbone.Collection 이다 

    + views/Header 형식은 각각 views/Header.js 로 연결되고 다시 templates/header.tmpl 파일과 연결된다 

       underscore micro template (tpl)을 사용하고 있다

// 마리오네트안에 

define(

  ['marionette','vent','collections/TodoList','views/Header','views/TodoListCompositeView','views/Footer'],

  function(marionette, vent, TodoList, Header, TodoListCompositeView, Footer){

    "use strict";


    // SPA 애플리케이션 1개를 생성한다 

    var app = new marionette.Application(),

        todoList = new TodoList();


    app.bindTo(todoList, 'all', function() {

      if (todoList.length === 0) {

        app.main.$el.hide();

        app.footer.$el.hide();

      } else {

        app.main.$el.show();

        app.footer.$el.show();

      }

    });


    // 리젼안에 화면을 담는다 

    app.addRegions({

      header : '#header',

      main   : '#main',

      footer : '#footer'

    });


 ... 중략 ...

});


  - Header.js 를 통한 Todo 입력하기 

    + todo 입력을 수행한다 

    + id="new-todo"   

    

    + 소스 

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

// views/Header.js

define(['marionette','templates'], function (Marionette,templates) {

  "use strict";


  // ItemView 을 상속 

  return Marionette.ItemView.extend({

    // templates/header.tmpl 파일을 참조 

    template : templates.header,


    // 템플릿 화면의 new-todo 아이디 

    // jQuery selected object를 가르키는 캐쉬된 속성을 input 이라고 생성한다 

    ui : {

      input : '#new-todo'

    },


    // 이벤트 설정 

    events : {

      'keypress #new-todo': 'onInputKeypress'

    },


    // 이벤트 핸들러 

    // 입력하고 엔터키 쳐지면 값을 컬렉션에 담는다 

    onInputKeypress : function(evt) {

      var ENTER_KEY = 13;

      var todoText = this.ui.input.val().trim();


      if ( evt.which === ENTER_KEY && todoText ) {

        // Collection에서 직접 Model객체를 생성함 

        this.collection.create({

          title : todoText

        });

        this.ui.input.val('');

      }

    }

  });

});


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

// templates/header.tmpl 

<h1>todos</h1>

<input id="new-todo" placeholder="What needs to be done?" autofocus>


  - TodoListCompositeView.js 를 통한 TodoList

    + header 밑으로 첨부된 todo list에 대한 태그입니다 

    


    + #todo-list 는 ul 태그안에  컬렉션이 리스팅된다 

    + 한개의 todo를 입력하였을 경우 : <ul id="todo-list> 태그안에  <li class="active">..</li> 내역이 동적으로 생성된다. 이것은 TodoItemView.js 가 된다. 

    + 즉, TodoListCompositeView.js는 TodoItemView.js 목록을 관리한다. itemView 속성에 지정한다

    + 좌측의 checkbox를 클릭하면 <li class="active"> 에서 <li class="completed">  로 변경된다 

    


// TodoListCompositeView.js

define(['marionette','templates','vent','views/TodoItemView'], function (Marionette,templates,vent,ItemView) {

  "use strict";


  return Marionette.CompositeView.extend({

    template : templates.todosCompositeView,

    itemView : ItemView,  // TodoItemView.js 

    itemViewContainer : '#todo-list',


    ui : {

      toggle : '#toggle-all'

    },


    events : {

      'click #toggle-all' : 'onToggleAllClick'

    },


    initialize : function() {

      this.bindTo(this.collection, 'all', this.updateToggleCheckbox, this);

    },


    onRender : function() {

      this.updateToggleCheckbox();

    },


    // 전체 checkbox 토글들에 대한 초기화 

    updateToggleCheckbox : function() {

      function reduceCompleted(left, right) { return left && right.get('completed'); }

      var allCompleted = this.collection.reduce(reduceCompleted,true);

      this.ui.toggle.prop('checked', allCompleted);

    },


    // 전체 todo 에 대한 토글

    onToggleAllClick : function(evt) {

      var isChecked = evt.currentTarget.checked;

      this.collection.each(function(todo){

        todo.save({'completed': isChecked});

      });

    }

  });

});


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

// templates/todoListCompositeView.tmpl

<input id="toggle-all" type="checkbox">

<label for="toggle-all">Mark all as complete</label>

<ul id="todo-list"></ul>


  - TodoItemView.js 

    + templates/todoItemView.tmpl 사용

    + todo를 더블클릭하면 class="active" 에서 class="active editing" 이 추가된다 

    


define(['marionette','templates'], function (Marionette,templates) {

  "use strict";


  return Marionette.CompositeView.extend({

    // 상단 <li class="active editing"> 태그를 표현 

    tagName : 'li',


    // todoItemView.tmpl 지정 

    template : templates.todoItemView,


    // <input class="edit" value="hi dowon"> 태그에 대한 jQuery selected object 를 가르킴

    ui : {

      edit : '.edit'

    },


    events : {

      'click .destroy' : 'destroy',

      'dblclick label' : 'onEditClick',

      'keypress .edit' : 'onEditKeypress',

      'click .toggle'  : 'toggle'

    },


    initialize : function() {

      this.bindTo(this.model, 'change', this.render, this);

    },


    onRender : function() {

      this.$el.removeClass('active completed');

      if (this.model.get('completed')) this.$el.addClass('completed');

      else this.$el.addClass('active');

    },


    destroy : function() {

      this.model.destroy();

    },


    toggle  : function() {

      this.model.toggle().save();

    },


    // 더블클릭을 하면 editing 중이라는 class가 추가되고 edit input 태그에 포커스가 가서 edit상태가 된다

    onEditClick : function() {

      this.$el.addClass('editing');

      this.ui.edit.focus();

    },


    // input 태그로 focus가 와서 입력후 enter를 치면 값을 넣고 editing class는 제거한다 

    onEditKeypress : function(evt) {

      var ENTER_KEY = 13;

      var todoText = this.ui.edit.val().trim();


      if ( evt.which === ENTER_KEY && todoText ) {

        this.model.set('title', todoText).save();

        this.$el.removeClass('editing');

      }

    }

  });

});


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

// todoItemView.tmpl

<div class="view">

  <input class="toggle" type="checkbox" <% if (completed) { %>checked<% } %>>

    <label><%= title %></label>

  <button class="destroy"></button>

</div>

<input class="edit" value="<%= title %>">


  - Footer.js 를 통한 Layout 개념이해 

    + Layout은 Region을 가지고 있다 

    + 좌측 완료안된 todo 건수 + 중앙 All, Active, Completed 상태별 todo 필터링 보기 + 우측 Clear Completed로 완료된 todo 제거 

    


    + 소스코드

// Footer.js 

define(['marionette','vent','templates','views/ActiveCount'], function (Marionette,vent,templates,ActiveCount) {

  "use strict";


  return Marionette.Layout.extend({

    // templates/footer.tmpl  

    template : templates.footer,


    // footer.tmpl의 id="todo-count" 밑의 <strong> 태그 

    regions : {

      count : '#todo-count strong'

    },


    // filters의 <a> 태그 

    ui : {

      filters : '#filters a'

    },


    // 제일 우측의 clear completed 

    events : {

      'click #clear-completed' : 'onClearClick'

    },


    initialize : function() {

      this.bindTo(vent, 'todoList:filter', this.updateFilterSelection, this);

    },


    // 리젼에 속한 count에 건수를 측정하는 ActiveCount 클래스에 컬렉션을 넘김

    onRender : function() {

      this.count.show(new ActiveCount({collection : this.collection}));

    },


    // All, Active, Completed <a> 태그의 class="selected"는 선택하는 곳으로 이동을 한다  

    // 또한 최상단의 <sectio id="todoapp" class="filter-active"> 또는 "filter-all", "filter-completed"로 변경됨 

    updateFilterSelection : function(filter) {

      this.ui.filters.removeClass('selected').filter('[href="#/' + filter + '"]').addClass('selected');

    },


    onClearClick : function() {

      vent.trigger('todoList:clear:completed');

    }

  });


});

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

// footer.tmpl

<span id="todo-count"><strong></strong> items left</span>

<ul id="filters">

  <li>

    <a href="#/">All</a>

  </li>

  <li>

    <a href="#/active">Active</a>

  </li>

  <li>

    <a href="#/completed">Completed</a>

  </li>

</ul>

<button id="clear-completed">Clear completed</button>


/////////////////////////
// views/ActiveCount.js
define(['marionette'], function (Marionette) {
  "use strict";

  return Marionette.View.extend({
    tagName : 'span',
    // collection에 변경이 있으면 다시 그려준다 
    initialize : function() {
      this.bindTo(this.collection, 'all', this.render, this);
    },
    render : function() {
      this.$el.html(this.collection.getActive().length);
    }
  });
});


쿨럭 목감기로 오늘은 여기까지 아무래도 Model/Collection 그리고 Marionette API 에 대한 분석이 필요해 보인다. 또한 TodoMVC 를 구현함에 있어서 개발 프로세스를 고려하여 분석하는 것이 맞을 듯하다. 다시 분석을 해보도록 한다 


<참조>

  - 원문 : Backbone.Marionette 

posted by 윤영식

오브젝트간의 이벤트 전달이 필요할 경우 loose coupling 을 유지하면서 커뮤니케이션이 가능토록 해주는 중재자 패턴에 대해 알아보자 



1) 중재자 패턴

  - 하나의 오브젝트 상태 변경시 다른 오브젝트가 해당 변경 정보를 알아야 할 경우

  - 별도의 중재자를 두어서 해당 중재자가 한 오브젝트의 변화에 대해 관심을 가지고 있는 오브젝트들한테 변경 정보를 전달한다

  

    + Mediator : Colleague 오브젝트와 통신할 수 있는 인터페이스를 정의한다 

    + ConcreteMediator : ConcreteColleagues 오브젝트들의 레퍼런스를 가지고 있다. 정보를 전달해 주는 역할을 수행

    + Colleague classes : Mediator 오브젝트의 레퍼런스를 가지고 있다가 다른 Colleague와 통신하길 원하면  Mediator를 통해서 통신을 한다 


  - 사용예

    + GUI Libraries : GUI 컴포넌트에서 하나가 선택되면 다른 것들이 enable/disable 되는 경우 

    + Chatting Application

       Chatroom : Mediator 로써 참석자들끼리 대화를 중재 인터페이스 정의

       ChatroomImpl : ConcreteMediator 한 참가자가 메세지 보내면 다른 참가들에게 메세지 전송하는 역할을 구현

       Participant : Colleague 참가자 인터페이스 정의

       HumanParticipant, Bot : ConcreteColleague 는 사람이 될 수도 bot이 될 수도 있다. Mediator 레퍼런스를 참조한다다



2) 구현상의 고려사항

  - Mediator 인터페이스정의는 Colleague 들이 여러개의 Mediator가 필요할 경우이다. 한개의 Mediator만 필요하다면 굳이 인터페이스 정의는 필요없다

  - Colleague의 상태가 변하면 정보를 Mediator에 전달하고 해당 정보에 관심이 있는 Colleague에 정보를 전달해 주는 Observer 패턴과 유사하다

  - 정보가 많을 경우 Asynch를 고려한다면 Message Queue 가 필요할 수 있다 

  - Colleague가 많아지면 Mediator 구현체가 복잡해 질 수 있다. 



3) Mediator.js 분석

  - 사이트 : https://github.com/ajacksified/Mediator.js

  - 중재자 패턴을 이용하여 WebSocket, AJAX call, DOM 이벤트를 쉽게 다루고 테스트 할 수 있도록 한다 

  - 채널(Channel)이 중재자가 되어서 Colleague들에게 관심 정보를 전파한다 

  - 설치하기 

$ npm install mediator-js

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

npm http 200 https://registry.npmjs.org/mediator-js/-/mediator-js-0.9.2.tgz

mediator-js@0.9.2 node_modules/mediator-js


  - Node.js에서 사용하기 

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

// md_node.js 파일

var Mediator = require("mediator-js").Mediator,

    mediator = new Mediator();


mediator.subscribe("wat", function(){ console.log(arguments); });

mediator.publish("wat", 7, "hi dowon", { one: 1 }); 


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

// 결과 (wat이 채널) 

$ node md_node.js

{ '0': 7,

  '1': 'hi dowon',

  '2': { one: 1 },

  '3':

   { namespace: 'wat',

     _subscribers: [ [Object] ],

     _channels: [],

     _parent:

      { namespace: '',

        _subscribers: [],

        _channels: [Object],

        _parent: undefined,

        stopped: false },

     stopped: false } }


  - 브라우져에서 AMD로 사용하기 

     + require.js 와 mediator.js 파일은 md_browser.html 파일 위치에서 js/ 폴더 밑에 존재함

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

// md_browser.html

<!doctype html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>Test</title>

</head>

<body>


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

<script>

        // base url을 반드시 주어야 한다 

requirejs.config({

   baseUrl: './js/'

});


        // 주의) mediator.js 에서 define을 Mediator 로 M을 대문자로 정의하였다 

require(['Mediator'], function(mediator) {

 mediator.subscribe("wat", function(){ console.log(arguments); });

 mediator.publish("wat", 7, "hi", { one: 1 });

});

</script>


</body>

</html>


   + subscribe/publish API

mediator.subscribe(channel, callback, <options>, <context>);

mediator.publish(channel, <data, data, ... >)

mediator.remove(channel, <identifier>)


   + on/bind 는 subscribe 의 alias 이고, trigger/emit 은 publish 의 alias, off 는 remove의 alias

   + Channel의 Namespace 로 : 를 사용한다 예) application:chat



<참조>

  - http://www.oodesign.com/mediator-pattern.html

  - 3실 청년 설명

  - 예제를 가지고 시퀀스 다이어그램을 통해서 설명

  - 다이어그램 보기 : UML 기초

  - 자바스크립트의 Mediator 패턴 설명

  - 자바스크립트 디자인패턴 - 설명

posted by 윤영식
2013. 3. 23. 17:12 Backbone.js

Backbone.js MV* 각 요소를 직접 개발해보고 전체 흐름에 대하여 알아보자



1. Backbone.Model

  - initialize : 초기화 model new 생성시 자동 호출 

  - save : 서버로 전송

  - fetch : 서버에서 가져오기

  - sync : 강제로 서버와 값을 맞춤 

  - id : 속성을 통하여 전송 아이디 설정가능 (MongoDB 를 사용하면 "idAttribute: _id" 로 설정한다)

  - defaults : model 객체의 기본값을 지정

  - validate : 데이터 정합성 체크

  - 테스트

    + index.html 준비 : jquery, underscore, backbone 순으로 최신버전을 다운로드 받아서 js 폴더 밑에 놓는다 

<!DOCTYPE html>

  <html>

  <head>

    <meta charset="utf-8">

    <title>hello-backbonejs</title>

  </head>

  <body>


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

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

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


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


  </body>

  </html>


    + 2.js 백본 코딩

(function($) { 

    var UserModel = Backbone.Model.extend({

        url: '/user',

        defaults: {

            name: '',

            email: ''

        }

    });


    var user = new UserModel();

    var userDetails = { name: "dowon", email: "ysyun@yuwin.co.kr"};

    user.save(userDetails, {

    success: function(user) {

    alert(user.toJSON());

    },

    error: function(user){

    alert(user.toJSON());

    }

    });


    var user1 = new UserModel({id: 1});

    user1.fetch({

    success: function(user) {

    alert('fetch user name : ' + user.get('name'));

    },

    error: function(user){

    alert(user.toJSON());

    }

    });


    user1.sync();

})(jQuery);


   + fetch시에 맨밑의 user(빨간색)는 서버에서 가져온 정보가 없다 (404 code)

  


    + save 시의 Request URL 정보 : http://localhost:8080/user

  



2. Backbone.View

  - initialize : render  메소드 호출

  - render : 화면 그리기

  - events : 화면의 이벤트와 핸들러 등록

    + index.html 안에 템플릿 추가 : 위치가 중요

<!DOCTYPE html>

  <html>

  <head>

    <meta charset="utf-8">

    <title>hello-backbonejs</title>

  </head>

  <body>


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

      <label><%= name %></label>

      <input type="text" id="search_input" />

      <input type="button" id="search_button" value="Search" />

    </script>

    <div id="search_container"></div>


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

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

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


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

  </body>

  </html>


   + 2.js 안에 View 추가 

    var SearchView = Backbone.View.extend({

        initialize: function(){

            this.render(); // 호출시 화면 그리기. initialize는 new 생성시 자동 호출됨 

        },

        render: function(){

            // 템플릿 과 데이터 결합

            var template = _.template( $("#search_template").html(), {name: 'dowon'} );

            this.$el.html( template );

        },

        events: {

            "click input[type=button]": "doSearch"  // 이벤트 핸들러 등록

        },

        doSearch: function( event ){

            // Button clicked, you can access the element that was clicked with event.currentTarget

            alert( "Search for " + $("#search_input").val() );

        }

    });


    var search_view = new SearchView({ el: $("#search_container") });


    + 브라우져 호출

  



3. Backbone.Router

  - routes : routing 경로와 처리 메소드 등록 (API 호출시 보여질 View 를 결정하여 준다)

    + 기존 호출 : http://localhost:8080 이고 

    + routes설정이 다음과 같으면 

            routes: {          

       "foo/:bar" : "paramtest",

       "*action" : "func"

   },

    + http://localhost:8080/#/foo/dowon 호출하면 paramtest 메소드로 이동

      http://localhost:8080/#/dowonaction 호출하면 func로 메소드로 이동

// sample example

var Router = Backbone.Router.extend({

   routes: {          

       "foo/:bar" : "paramtest",

       "*action" : "func"

   },

   func: function (action) {

       console.log('action> ' + action);

   },

   paramtest:function (p) {

       console.log('p> ' + p);

   }

});

  

  - Backbone.history.start() 호출 : SPA에서 Fragment (# 해쉬로 표현) 들에 대한 브라우져 호출의 히스토리를 관리한다

  - 라우팅 과 Fragment의 관계를 반드시 이해하자 

  

    이미지 출처: http://blog.nodejitsu.com/scaling-isomorphic-javascript-code


  - Dynamic Routing

 var AppRouter = Backbone.Router.extend({

        // # 해쉬로 표현될 Fragment를 등록한다

        routes: {

            "posts/:id": "getPost",

            "*actions": "defaultRoute" // Backbone will try match the route above first

        }

    });


    // Instantiate the router

    var app_router = new AppRouter;

    app_router.on('route:getPost', function (id) {

        // Note the variable in the route definition being passed in here

        alert( "Get post number " + id );   

    });

    app_router.on('route:defaultRoute', function (actions) {

        alert( actions ); 

    });

    // Start Backbone history a necessary step for bookmarkable URL's

    Backbone.history.start();


  - 브라우져 호출 : <url>/spa-page/#/posts/1  호출

  



4. Backbone.Collection

  - model : 속성에 Model 클래스를 지정한다 

  - models : 컬렉션에 등록된 model 객체 배열열

  - 2.js Model -> Collection 에 지정한다 

     var Song = Backbone.Model.extend({

     initialize: function(){

         console.log("Music is the answer");

     }

     });


     var Album = Backbone.Collection.extend({

 model: Song

     });


      var song1 = new Song({ name: "How Bizarre", artist: "OMC" });

      var song2 = new Song({ name: "Sexual Healing", artist: "Marvin Gaye" });

      var song3 = new Song({ name: "Talk It Over In Bed", artist: "OMC" });


      var myAlbum = new Album([song1, song2, song3]);

      console.log( myAlbum.models );

  

  - 브라우져 호출

  



Backbone을 이용하게 되면 좀 더 쉽게 SPA 를 만들 수 있다. Backbone을 사용하여 개발하는 Workflow는 다음과 같다

  - index.html 을 만든다 : SPA 에서는 오로지 <body> 태그는 index.html 에만 존재한다 

  - Backbone.Router를 개발한다 : RESTful 설계서에 따라 업무를 분류하면 될 것이다

  - Backbone.View를 개발한다 : view에 맞는 template 파일을 별도로 가져가면 되겠다

  - Backbone.Collection을 개발한다 : Model을 만들고 Collection을 개발한다

  - RESTful Web Services & Open API : 설계에 따라서 Model에 url 을 설정하면 되겠다  

이미지 출처 : 참조의 동영상중에서 


  

  Backbone의 서버연동 전체흐름도 : Real-time은 화면의 reload가 없이 진행된다. The 'net을 socket.io로 연결하는 것이 중요!



5. 총정리

  - Model, View, Router 이해


    + 상황에 따른 코딩 패턴 소개
    + 코드 컨벤션 및 네이밍 규칙

    + Backbone Pattern에 따라 전체 디렉토리 구조 만들기

    + 시작, 업무클래스의 AMD 사용방법 소개 



<참조> 

  - 원문1 : Hello World Backbone.js Tutorial

  - 원문1의 GitHub 저장소

  - 원문2 비디오 튜토리얼

  - 원문2의 GitHub 저장소

  - Backbone 완벽가이드 문서

  - $(this.el) 과 this.$el 의 차이점  : Backbone.View 의 render 메소드에서 사용함. this.$el 에서 에러 발생하면 최신버전 upgrade

  - Backbone 개발 Workflow 동영상 

  - Backbone Step by Step 튜토리얼 (동영상포함)

  - Backbone/Node.js/MongoDB 페이스북 공개 그룹

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. 20. 23:18 Testing, TDD/Test First

  TDD 또는 BDD로 진행을 하는 것은 소프트웨어의 명세를 만들어 가면서 보다 안정적인 서비스를 만드는 지름길이다. 물론 좀 더 시간을 투자해야하는 지름길이지만 나중의 유지보수를 생각하면 긴안목에서 역시 빠른 지름길이다. 특히 모바일 서비스의 경우는 몇번의 에러가 발생하면 사용자는 해당 앱을 삭제하고 절대로 사용하지 않는다는 보고가 있다. 그리고 페이스북에는 QA조직이 없고 개발자가 직접 모든 테스트를 수행하고 책임을 지도록 되어있다. 

  우리가 생산성을 위하여 프레임워크를 사용하듯, 안정성을 위해서는 테스트 코드가 필수이다. 또한 테스트 코드에 주석을 달고 Groc 같은 유틸로 문서화를 한다면 소프트웨어의 명세를 따로 하지 않아도 될 것이다. 

  백본기반의 개발에 있어서도 모델, 뷰, 라우터등의 TDD는 필요하며 이를 어떻게 하는지 따라해 보자



1) TDD 준비하기

  - backbone-mocha.zip 파일을 다운로드 받는다 

backbone-mocha.zip


  - tests/test-runner.html 을 수행한다 

  


  - test-runner.html 파일 이해

<!DOCTYPE html>

<html lang="en">

<head>

    <!-- Title &amp; Meta -->

    <title>Frontend tests</title>

    <meta charset="utf-8">


    <!-- Stylesheets -->

    <link rel="stylesheet" href="libs/mocha/mocha.css">

</head>

<body>


    <!-- mocha 결과 화면을 뿌리기 위한 div --> 

    <div id="mocha"></div>


    <!-- Testing Libraries 첨부 -->

    <script src="libs/mocha/mocha.js"></script>

    <script src="libs/chai/chai.js"></script>

    <script src="libs/sinon/sinon.js"></script> <!-- 본 예제에서는 sinon.js 안씀. 즉, 삭제가능 -->


    <!-- chai를 사용하고 mocha는 TDD를 사용한다. BDD는 bdd 소문자로 입력 

          TDD를 하면 mocha에서 suit(), setup(), teardown(), test() 4가지 메소드를 사용한다 

          첨부파일의 user.test.js 파일 참조


          chai는 should, expect, assert 방식을 선택할 수 있는데 여기서는 expect 방식을 선택

    -->

    <script>

        // Use the expect version of chai assertions - http://chaijs.com/api/bdd

        var expect = chai.expect;


        // Tell mocha we want TDD syntax

        mocha.setup('tdd');

    </script>


    <!-- 사용하는 Libs -->

    <script src="../libs/jquery/jquery-1.8.3.min.js"></script>

    <script src="../libs/underscore/underscore-min.js"></script>

    <script src="../libs/backbone/backbone-min.js"></script>


    <!-- 코딩한 원본 Source files -->

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

    <script src="../src/models/user.js"></script>

    <script src="../src/views/profile.js"></script>


    <!-- 개발한 소스와 맵핑되는 Test files -->

    <script src="models/user.test.js"></script>

    <script src="views/profile.test.js"></script>


    <!-- 최종적으로 mocha 를 수행한다 -->

    <script>

        mocha.run();

    </script>


</body>

</html>



2) Backbone Model TDD

  - tests/models/user.test.js 밑에 getFullName 테스트 코드 첨부

    + suite, setup, teardown, test : Mocha interface

    + expect : Chai interface

suite('User Model', function() {


    setup(function() {

        this.user = new app.models.User({

            first_name: "Yun",

            last_name: "DoWon"

        });

    });


    teardown(function() {

        this.user = null;

    });


    test('should exist', function() {

        expect(this.user).to.be.ok; // Tests this.user is truthy  <- chai 코드

    });


    test('calling getFullName should return first_name[space]last_name', function() {

        expect(this.user.getFullName()).to.equal('Yun DoWon');

    });


});

  

   - 호출하면 빨간색 Fail 발생

  - 에러 해결하여 초록 Green 으로 만들기위해 user.js 안에 getFullName 메소드 구현

(function(global, _, Backbone, undefined) {


    app.models.User = Backbone.Model.extend({

        getFullName: function() {

               // model 내역은 user.test.js의 setup에서 이미 설정해 놓았음

        return this.get('first_name') + " " + this.get('last_name');

        }

    });


})(this, _, Backbone);


  - 브라우져 호출 결과 : "User Model" 제목은 user.test.js 에서 지정한 제목임

  



3) Backbone View TDD

  - test/views 폴더 밑에 profile.test.js 파일을 만든다 

  - 다음 코드를 넣는다

suite('Profile View', function() {

 

    // Create a User model to pass into our view to give it data

    var model = new app.models.User({

        first_name: 'Yun',

        last_name: 'YoungSik',

        age: 23

    });

 

    setup(function() {

        this.profile = new app.views.Profile({

            // Pass in a jQuery in memory <div> for testing the view rendering

            el: $('<div>'), 

            

            // Pass in the User model 

            // dependency inversion makes this simple to test, 

            // we are in control of the dependencies rather 

            // than the view setting them up internally.

            model: model 

        });

    });

 

    teardown(function() {

        this.profile = null;

    });

 

    test('should exist', function() {

        expect(this.profile).to.be.ok;

    });

 

});


  - 브라우저에서 호출하기전 test-runner.html 에 profile.js 와 profile.test.js 를 넣는다

    <!-- Source files -->

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

    <script src="../src/models/user.js"></script>

    <script src="../src/views/profile.js"></script>


    <!-- Test -->

    <script src="models/user.test.js"></script>

    <script src="views/profile.test.js"></script>


  - 브라우져 호출 결과

  - src/views 폴더에 profile.js 파일을 생성하고 다음 코드는 넣고 다시 호출한다

(function(global, $, _, Backbone, undefined) {

 

    app.views.Profile = Backbone.View.extend({

 

    });

 

})(this, $, _, Backbone);

    

  - 정상 호출됨  

 


  - profile.test.js 안에 구현하고 싶은 다음 테스트 코드를 넣고 호출한다

    test('should exist', function() {

        expect(this.profile).to.be.ok;

    });

    // view 에 대한 render 테스트 : Regex 로 해당 내역이 화면출력으로 나왔는지 테스트

    test('render()', function() {

        this.profile.render();

     

        // 렌더링된 결과화면에 대하여 정규표현식으로 검사를 하는 것이 핵심!

        expect(this.profile.$el.html().match(/Yun/)).to.be.ok;

        expect(this.profile.$el.html().match(/YoungSik/)).to.be.ok;

        expect(this.profile.$el.html().match(/23/)).to.be.ok;

    });


  - profile.js 의 내역을 구현지 않았기 때문에 테스트에 대한 결과는 빨간색 fail 

  


  - profile.js 의 render() 메소드를 구현한다 

(function(global, $, _, Backbone, undefined) {

    app.views.Profile = Backbone.View.extend({

 

        render: function() {

            var html = "<h1>"+ this.model.getFullName() +"</h1>" +

                "<p>"+ this.model.get('first_name') +" is "+ this.model.get('age') + " years old";

            this.$el.html(html);

        }

 

    });

})(this, $, _, Backbone); 


  - 브라우져 호출 결과

  

 


테스트 코드를 만들고 실행하여 에러(빨간색)가 발생하면 테스트 코드에 대응하는 업무 코드를 짜고, 다시 실행하여 정상(녹색)적으로 돌아가게 한다. 정상에는 두 단계가 있는데, 비즈니스 로직이 없이 API와 정적값을 통해 정상(녹색) 통과를 하고 나면 이후 비즈니스 로직을 넣고 다시 정상(녹색) 통과를 하는지 체크한다. 



<참조> 

  - 원문 : Testing Backbone.js with mocha 

  - http://visionmedia.github.com/mocha/

  - http://chaijs.com/

posted by 윤영식
2013. 3. 20. 15:09 Backbone.js

기존에 작성한 index.html 파일안에는 템플릿에 대한 내역도 같이 포함되어 있다. 해당 템플릿 내역을 별도의 파일로 때어 내어 AMD  모듈화와 결합하는 방법을 알아본다. 



1) Require.js 플러그인 test.js 설치

  - 텍스트 파일을 로딩하는 text.js 파일을 적절한 위치에 설치 (여기서는 require.js 랑 같은 위치에 설치함)

  - 다운로드 : http://requirejs.org/docs/download.html#text

  - index.html 파일의 requirejs.config 에 의존성 설정 추가
requirejs.config({
    baseUrl: './js/', 
    paths: {
        'jquery': 'jquery',
        'underscore': 'underscore',
        'backbone': 'backbone',
        'text': 'text',
        'jquery.dateformat' : 'jquery-dateformat'
    },
    shim: {
        'underscore': {
            exports: '_'
        },
        'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        'jquery.dateformat': {
            deps: ['jquery']
        }
    }
});



2) index.html 에서 템플릿내역 분리하기 

  - 템플릿 영역 분리하여 ride_template.html 명칭으로 index.html 과 같은 위치에 저장

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

// index.html 내부

<div id="rides">

    <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>

    <!-- 주석처리 사용하지 않음 / 또는 제거함 

        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-->

</div>


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

// ride_template.html 전체 내역

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

<ul>

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

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

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

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

</ul>

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



3) ride.js 파일에 템플릿 의존성 주입

  - AMD는 SOLID의 Dependency Inversion Principle과 같은 원리. 즉, 스프링의 Inversion Of Control 과 같은 방식이다. 

  - 따라서 모듈 ride_template.html 을 ride.js 에 주입해 보자 

    +  define([의존관계], 파라미터) : 의존관계에 템플릿 파일 상대 경로 지정

    + 기존 template 프로퍼티의 값을 파라미터로 변경

define(['jquery', 'backbone', 'underscore', 'text!../ride_template.html', 'jquery.dateformat'], function($, Backbone, _, rideTemplate) {

   .. 중략 ..

   var RideView = Backbone.View.extend({

        tagName:"div",

        className:"rideContainer",

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

        template: rideTemplate,


   render:function () {

        .. 중략 ..



4) 브라우져 호출 

  - 호출후 크롬 개발자 도구의 DOM 구성 : rideContainer div 태그안에 ride_template.html 이 들어가 있다 

   


 ** 전체 소스 

riding.zip



<참조>

  - 백본에 템플릿 텍스트 적용하기

  - text.js 사용법 : prefix로 반드시 text!을 붙여서 정적파일을 로딩한다 


posted by 윤영식
2013. 3. 20. 09:36 HTML5, CSS3/jQuery

자바스크립트 코드에서 이벤트 핸들러처리가 끝난 후 preventDefault() 와 return false 등의 코드를 본적이 있을 것이다. 간혹 stopPropagation() 호출도 보이는데 이들의 차이점을 알아보자 



1) preventDefault vs return false, stopPropagation : jQuery를 사용하지 않은 경우 

  - stopPropagation 은 사용자 정의 이벤트의 bubbling up 되는 것을 막아준다 

    + 이벤트와 이벤트 핸들러를 찾기 위한 캡쳐링과 버블링에 대해 이곳을 참조한다.

    + 사용자 정의 이벤트는 개발자가 직접 이벤트 핸들러를 작성할 경우

    + 즉, 사용자가 작성한 이벤트 핸들러의 동작을 막아준다

  - preventDefault 는 기본 정의 이벤트의 동작을 막아준다 

    + <a href..> 앵커 태그와 같은 기본 제공 이벤트 (사이트로 이동하는 것) 

    + 즉, 기본 이벤트의 동작을 막아준다 

  - return false 는 jQuery 사용할 때와 안할 때의 반응이 틀리다

    + jQuery 사용안할 때는 preventDefault와 동일하게 동작한다 

  

  - 주석 a, b, c 케이스 테스트 : 화면의 "[Click me]" 레이블을 클릭할 경우

 

<html>

<body>

<a href="http://mobicon.tistory.com">

<div id="div1">

<input type="label" id="label1" value="[Click me]"/>

</div>

</a>


<script>

document.getElementById('div1').onclick = function() { 

alert('click div1'); 

};

document.getElementById('label1').onclick = function(e) { 

alert('click label1'); 

// a 

//e.stopPropagation();

// b

//e.preventDefault();

// c

//return false;

};

</script>

</body>

</html>


   + e.stopPropagation() 만 주석 제거

      "click label1" 경고창 뜨고 "mobicon.tistory.com" 사이트로 이동 ("click div1" 경고창 안뜸. 즉, 버블링 업 막아줌)

   + e.preventDefault() 만 주석 제거

      "click label1" 경고창 뜨고 "click div1" 경고창 뜸 그러나 "mobicon.tistory.com"으로 이동하지 않음 (즉, 기본 이벤트 막아줌)

   + return false 만 주석 제거

      "click label1" 경고창 뜨고 "click div1" 경고창 뜸 그러나 "mobicon.tistory.com"으로 이동하지 않음 (즉, 기본 이벤트 막아줌)


  - jQuery를 사용하지 않은 e.preventDefault와 return false 의 동작은 동일하다



2) preventDefault vs return false, stopPropagation : jQuery를 사용할 경우 

  - stopPropagation 상동

  - preventDefault 상동

  - return false 는 jQuery 사용할 때와 안할 때의 반응이 틀리다

    + jQuery 사용할 때는 preventDefault와 동일하게 동작한다 

<html>

<body>

<a href="http://mobicon.tistory.com">

<div id="div1">

<input type="label" id="label1" value="[Click me]"/>

</div>

</a>


<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>

<script>

$('#div1').click( function() { 

alert('click div1'); 

} );

$('#label1').click( function(e) { 

alert('click label1'); 

//e.stopPropagation();

//e.preventDefault();

//return false;

} );

</script>

</body>

</html>

   

   + e.stopPropagation() 만 주석 제거

      상동

   + e.preventDefault() 만 주석 제거

      상동

   + return false 만 주석 제거

      "click label1" 경고창 뜨고 "click div1" 경고창은 안 뜸 그리고 "mobicon.tistory.com"으로 이동하지 않음 


  - 결론적으로 jQuery를 사용하여 return false를 쓸 경우 stopPropgation() 와 preventDefault() 가 동시에 적용됨 



<참조>

  - stopPropagation()과 preventDefault() 차이

  - preventDefault() vs return false 차이

  - 자바스크립트 이벤트 핸들링

posted by 윤영식
2013. 3. 18. 14:42 Backbone.js

엔터프라이즈급의 SPA 개발을 위해서는 AMD를 필수적으로 사용해야 한다. 기존 "서점 샘플 응용하기 - 02"에서 진행하였던 자바스크립트 라이브러리들과 Backbone 기반 코드인 ride.js 파일을 Require.js를 이용하여 모듈화하고 재설정해 보자 



1) index.html 변경하기 

  - 기존 index.html 내역

    + <%= %> 구문으로 underscore template 사용

    + jquery dateformat plugin 을 html에서 직접 호출

    + <script> 의존 라이브러리를 개발자가 알아서 체크하고 의존성이 있는 것을 더 밑으로 열거함 

       자바스크립트만을 가지고 SPA 를 개발할 때 수 많은 의존관계를 지금의 방식으로 설정하는 것은 한계가 있음 

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8"/>

    <title>Backbone.js Cycle Riding for Dowon</title>

    <link rel="stylesheet" href="css/screen.css">

</head>

<body>


<div id="rides">

    <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>

    <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>  <!-- jquery dataformat 직접 호출 --> 

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

        </ul>

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

    </script>

</div>


<!-- 자바스크립트의 의존관계에 따라 의존성이 있는 라이브러리를 맨밑에 열거한다 --> 

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

<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>

</body>

</html>


  - Require.js 방식으로 index.html 변경 

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8"/>

    <title>Backbone.js Cycle Riding for Dowon</title>

    <link rel="stylesheet" href="css/screen.css">

</head>

<body>


<div id="rides">

    <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>

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

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

        <ul>

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

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

            <li><%= ridingDate %></li>  <!-- jquery dateformat은 ride.js 에서 포멧팅한다 --> 

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

        </ul>

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

    </script>

</div>


<!-- require.js 최상단에 위치 --> 

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

<script>

// 환경설정

requirejs.config({

    // 라이브러리 위치 (index.html 에서 상대경로)

    baseUrl: './js/', 

    // baseUrl+paths 로 실제 라이브러리 파일위치를 지정 (value값에서 .js 생략함)

    paths: {

        jquery: 'jquery',

        underscore: 'underscore',

        backbone: 'backbone',

        'jquery.dateformat' : 'jquery-dateformat'

    },

    // require.js 를 사용할 수 없는 이미 정의된 라이브러리를 require.js 방식으로 만들고자 할 때 설정

    shim: {

        'underscore': {

            exports: '_'

        },

        'backbone': {

            deps: ['underscore', 'jquery'],  // 의존하는 라이브러리 설정

            exports: 'Backbone'  // window객체에서 접근가능한 변수명 설정 

        },

        'jquery.dateformat': {   // jquery.dateformat 을 index.html 에서 사용하지 않고 ride.js 에서 사용하기 위해 설정

            deps: ['jquery']

        }

    }

});


// define 펑션으로 정의한 ride.js 모듈을 사용하기 위하여 require 펑션을 호출한다 

require(['ride'], function(ride) {

    console.log('let\'s go riding');

});

</script>


</body>

</html>



2) ride.js 모듈화 하기 

  - 기존 ride.js 

(function ($) {


    var Ride = Backbone.Model.extend({

    .. 중략 ..


})(jQuery);  // 글로벌 변수 jQuery 를 받아서 사용함 


  - define 펑션을 사용하여 ride.js 모듈화 하기 

// jquery, backbone을 사용한다

// jquery.dateformat은 jquery 플러그인으로 의존관계만 설정해 놓고 파라미터로 받지는 않는다 

// 파라미터로 jquery=$, backbone=Backbone, underscore=_ 로 레퍼런스를 받고 있다 

define(['jquery', 'backbone', 'underscore', 'jquery.dateformat'], function($, Backbone, _) {


    var Ride = Backbone.Model.extend({

    .. 중략 ..


    renderRide: function(item){

            // 화면에 포멧 변경로직을 넣지 않고 model에서 변경하여 준다

            // update ridingDate to YYYY/MM/dd from YYYY-MM-DDT00:00:00.000Z

            var ridingDate = item.get('ridingDate');

            var date = $.format.date(new Date(ridingDate), 'yyyy/MM/dd');

            item.set('ridingDate', date);


            var rideView = new RideView({

                model: item

            });

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

     },

    .. 중략 ..


});



3) 호출하기 

  - 브라우져에서 호출

    + nodemon 으로 node 수행 

$ nodemon server.js

18 Mar 11:40:03 - [nodemon] v0.7.2

18 Mar 11:40:03 - [nodemon] watching: /Users/nulpulum/development/backbone/riding

18 Mar 11:40:03 - [nodemon] starting `node server.js`

Express server listening on port 8080 in development mode

18 Mar 13:28:12 - [nodemon] restarting due to changes...

18 Mar 13:28:12 - [nodemon] /Users/nulpulum/development/backbone/riding/public/js/ride.js


    + http://localhost:8080/  : 하나더 추가해 보았다 

  


<참조>

  - Require.js API

  - backbone.js 소스 주석 한글화

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 윤영식