블로그 이미지
Peter Note
Web & LLM FullStacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

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

오브젝트간의 이벤트 전달이 필요할 경우 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 Peter Note