2012년 2월 9일 목요일

한방에 뷰에서 데이터 베이스까지?! Model-View Autosubscribe.

요몇일 Meteor에 완전 정신없이 빠져있었는데
불만도 많고 엉성하기도 하지만 예전에 만들려고 했었던 프레임웤이랑 사상이 너무 비슷한 부분이 많아서 마치 뇌스캔 당한 기분이기도 했다.
몇가지 특징을 순서없이 꼽아보자면

  • 서버와 클라이언트가 마치 하나의 어플리케이션처럼 이어져있다. 마치 옛날의 RAD환경처럼.
  • 저장=디플로이. 사실 이거 되는 건 많은데 서버랑 클라이언트가 동시에 적용된다.
  • 뷰-모델 바인딩. Database 를 수정하면 뷰까지 함께 바뀐다.
  • backbone.js를 이용한 클라이언트쪽 Routing.
  • 클라이언트 콘솔에서 직접 데이터베이스 관련 커맨드를 날려볼 수 있다. 아직은 안막아 놓았는데 개발 편의를 위해선 좋지만 보안면에선 완전 꽝이라고 생각할 수 있지만 언제든지 meteor remove insecure하면 된다.
  • 간편한 디플로이. 과연 호스팅쪽 백엔드를 어떻게 할지는 모르겠지만 현재는 meteor deploy <appname>.meteor.com 명령만으로 디플로이 가능
  • 이하 인상적인 패키지들. 매우 참고가 되었음.
  • minimongo
  • fiber 를 이용한 coroutine

더 많지만 내겐 이정도가 인상적이었다.
그중 MVVM프레임워크인 knockout.js 등에서도 Meteor 에서도 흥미로왔던 모델=뷰 바인딩(이하 Autosubscribe라고 하겠음)기법에 대해서 한번 구현해 보고자 한다.
마치 마법같은 일인데 일단 모델만 써보자 (요즘 쓰고 있는 CoffeeeScript 스타일로 ,를 뺐다)
employees = {
  employee : [
    {
      "name" : "Scott"
      "department" : "TF Team"
    }
    {
      "name" : "Tiger"
      "department" : "SE Team"
    }
  ]
}
이런 모델을

<ul class="employees">
  <li class="employee">
    <ul>
      <li class="name">
        Scott
      </li>
      <li class="department">
        TF Team
      </li>
    </ul>
  </li>
  <li class="employee">
    <ul>
      <li class="name">
        Tiger
      </li>
      <li class="department">
        SE Team
      </li>
    </ul>
  </li>
</ul>
모델에서 데이터를 변경하면 위와 같이 바로 뷰에서도 반영이 되도록 하려면 어떻게 해야할까?
무한 루프를 돌면서 계속 모델을 감시하다가 변경이 있을때 뷰를 갱신하게 할 수도 있겠지만 너무 무식한 방법이다.

사실 아이디어는 단순한 부분부터 시작한다.
console.log(employees.employee[0].name);
employees.employee[0].name = "David";

이런 식으로 접근하는 걸 setter/getter 로 보면
console.log(employees.employee[0].getName());
employees.employee[0].setName("David");

사실 이것과 크게 다르지 않다.
employee 객체를 보면 이런 식이 되겠지.
var employee = function() {
  return {
    name:"",
    department:"",
    setName:function(val) {
      this.name = val;
    },
    getName:function() {
      return this.name;
    }
  };
};

var emp = new employee();
emp.setName("Scott");
console.log(emp.getName());

http://jsbin.com/amopak 에 일단 링크.

employee.setName 을 할때 특정 객체를 바인딩하면 좋겠다.
var employee = function() {
  return {
    name:"",
    department:"",
    setName:function(val) {
      this.name = val;
      $(".name").text(val);
    },
    getName:function() {
      return $(".name").text();
    }
  };
};

var emp = new employee();
emp.setName("Scott");
console.log(emp.getName());

바인딩이라고 이런식으로 구현이 가능하겠다. 물론 input 이면 text 대신 val을 디테일로 가면 더 복잡하겠다.
여러개가 있을 수 있으니 특정 DOM아래에 붙도록 해보자.

var employee = function(target) {
  return {
    name:"",
    department:"",
    setName:function(val) {
      this.name = val;
      $(target).find(".name").text(val);
    },
    getName:function() {
      return $(target).find(".name").text();
    }
  };
};
http://jsbin.com/amopak/3 중복을 만들고 중복을 계속 제거해나가는 식의 접근을 하고 있다.

http://jsbin.com/amopak/7

다른 속성도 넣고 중복도 제거해보자.
var employee = function(target) {
  return {
    set:function(key,val) {
      target.find('.'+key).text(val);
    },
    get:function(key) {
      return target.find('.'+key).text();
    }
  };
};
http://jsbin.com/amopak/5 employee의 set 과 get method 로 하위 셀렉터를 접근할 수 있다.

Meteor관련 링크 첨부.