MooJoe 0.9.0 릴리즈
October 30th, 2008얼마 전 강규영 님은 JavaScript 객체와 DOM Element를 맵핑시켜주는 JOE를 공개하셨습니다. JOE는 DOM Element를 모델 스토리지로 사용할 수 있게 해주는 굉장히 유용한 프로젝트입니다.
하지만 아쉽게도 jQuery 기반으로 만들어져 MooTools 1.2를 사용하는 VLAAH나 개인 작업에는 사용할 수 없었죠. 저는 곧 바로 JOE를 MooTools 스타일로 포팅하는 MooJoe 프로젝트를 시작했는데 한참 잊고 있다가 이제서야 공개합니다.
Simple Mapping
다음과 같은 HTML 코드가 주어졌습니다.
<div id="names">
<p class="name">
<span class="first">Heungsub</span>
<span class="last">Lee</span>
</p>
<p class="name">
<span class="first">Alan</span>
<span class="last">Kang</span>
</p>
</div>

척 보기에도 서버 측에서 두 개의 Name 객체를 div#names에 출력한 모습입니다. MooJoe는 JOE와 마찬가지로 DOM Element를 JavaScript 객체에 동기화시켜줍니다. 동기화를 하기 위해 먼저 Name 클래스를 정의합니다.
var Name = new MooJoe.Class({
first: '.first',
last: '.last'
});
MooJoe의 클래스는 MooJoe.Class 생성자로 만들 수 있습니다. 인자로 맵핑 룰을 지정할 수 있는데 Name 클래스의 경우 first 속성에 .first라는 CSS 셀렉터를, last 속성에 .last라는 CSS 셀렉터를 지정했습니다. CSS 셀렉터를 맵핑룰로 쓰기 때문에 .last가 항상 두 번째 span이라면 다음과 같이 써도 무관합니다.
var Name = new MooJoe.Class({ first: 'span.first‘, last: ‘span:nth-child(2)‘ });
이제 div#names의 내용을 방금 만든 Name 클래스의 인스턴스로 만들어봅시다.
var names = [
$$('#names p.name')[0].toObject(Name),
$$(’#names p.name’)[1].toObject(Name)
];
// [name_instance#1, name_instance#2]
MooJoe는 MooTools의 Element 클래스를 확장해 Element.prototype.toObject 메서드를 제공합니다. 이 메서드에 맵핑시킬 MooJoe 클래스를 넘기면 맵핑된 객체를 반환합니다. 물론 다른 MooTools의 Element 메서드처럼 Elements.prototype.toObject로 짧게 쓰는 것도 가능합니다. 다음 코드는 방금 전 예제와 동일합니다.
var names = $$('#names p.name').toObject(Name);
맵핑시킨 속성들에 접근해볼까요? getter/setter는 MooTools 1.2의 컨벤션을 따릅니다.
names[0].get(’first’);
// ‘Heungsub’
names[0].get(’last’);
// ‘Lee’
names[0].set(’first’, ‘Haesam’);
names[0].get(’first’);
// ‘Haesam’
또한 속성 값을 변경할 경우 맵핑된 Dom Element에도 즉각 반영됩니다.
...
<p class="name">
<span class="first">Alan</span>
<span class="last">Kang</span>
</p>
...
이 Element는
$$('#names p.name .first')[1].get(’html’)
// ‘Alan’
names[1].set(’first’, ‘Sungryong’);
$$(’#names p.name .first’)[1].get(’html’)
// ‘Sungryong’
이렇게 변합니다.
...
<p class="name">
<span class="first">Sungryong</span>
<span class="last">Kang</span>
</p>
...

여기까지는 단순히 자식 Element의 내용으로 속성을 맵핑시키는 간단한 사용법이었습니다. 하지만 속성이 항상 자식 Element에 표현되지만은 않겠죠. 가령 이름과 홈페이지 주소를 표현하는 경우를 생각해볼 수 있습니다.
<a class="person" href="http://heungsub.net/">이흥섭</a>

a.person으로부터 name과 homepage 속성을 갖는 Person 객체를 만들고싶습니다. 어떻게 해야할까요?
Complex Mapping
MooJoe는 복잡한 맵핑룰을 제공합니다. 위 a.person을 객체로 만들기 위해 먼저 Person 클래스를 정의합니다.
var Person = new MooJoe.Class({
name: ['', 'text'],
homepage: ['', 'href']
});
맵핑룰에 CSS 셀렉터 대신 배열이 쓰였습니다. 이 경우 name 속성은 “이 Element의 text 값”에 맵핑시키고 homepage 속성은 “이 Element의 href 값”에 맵핑시킨다는 뜻입니다. 배열의 첫 번째 원소인 빈 문자열이 바로 “이 Element”를 가리킵니다. text와 href는 MooTools의 getter/setter로 접근 가능한 키워드로, 각각 innerHTML과 href Attribute를 의미합니다.
var hs = $$('a.person')[0].toObject(Person);
// person_instance#1
hs.get(’name’);
// ‘이흥섭’
hs.get(’homepage’);
// ‘http://heungsub.net/’
조금 더 복잡한 Person 예제를 볼까요?
<div class="person">
<p class="face">
<img src="http://farm4.static.flickr.com/3122/2894552409_3439382280_s.jpg" />
</p>
<p class="name">
<a href="http://heungsub.net/">
<span class="first">Heungsub</span>
<span class="last">Lee</span>
</a>
</p>
<p class="age">18</p>
</div>

이제 Person은 이름과 홈페이지 주소 뿐 아니라 사진, 나이, 게다가 Name 객체까지 표현해야합니다. 다행히 MooJoe으로 맵핑한 각 값을 특정한 타입이나 클래스로 캐스팅해줄 수 있습니다. Person 클래스를 다시 정의해봅시다.
var Person = new MooJoe.Class({
name: ['.name a', Name],
homepage: ['.name a', 'href', String],
face: ['.face img', 'src', String],
age: ['.age', Number]
});
homepage와 face의 룰은 3개의 원소로 되어있습니다. 각각 맵핑할 Element, 맵핑할 Attribute, 타입(클래스)을 나타냅니다. 맵핑할 Element인 첫 번째 원소를 제외하고는 순서가 바뀌어도 상관 없으며 기본값으로 각각 'text'와 String을 갖기 때문에 다음과 같이 써도 동일합니다.
...
homepage: ['.name a', String, 'href'],
face: ['.face img', 'src'],
…
name의 캐스팅 클래스는 Simple Mapping 예제에서 정의해뒀던 Name 클래스입니다. 자 그럼 객체로 구워볼까요?
var hs = $$('.person')[0].toObject(Person);
hs.get(’name’);
// name_instance#3
hs.get(’name’).get(’first’);
// ‘Heungsub’
hs.get(’face’);
// ‘http://farm4.static.flickr.com/3122/2894552409_3439382280_s.jpg’
hs.get(’age’);
// 18
name 속성은 Name 객체로, face는 문자열로, age는 숫자로 잘 캐스팅되는군요.
(강규영 님 블로그에서 보고 너무 재미있어서 퍼왔습니다)
Detached Object
MooJoe 클래스를 MooTools 클래스처럼 사용할 수도 있습니다. 임의의 Name 객체를 만들어보겠습니다.
var new_name = new Name('Dachimawa', 'Lee');
new_name.attached;
// []
new_name.get(’first’);
// ‘Dachimawa’
new_name.get(’last’);
// ‘Lee’
생성자에 보낼 인자 순서는 맵핑룰 순서와 동일합니다. first 속성이 첫 번째 룰이었으므로 'Dachimawa'가 first 값이 됩니다.
attach() 메서드로 연결되어있지 않은 MooJoe 객체는 언제라도 Dom Element에 동기화시킬 수 있습니다.
...
<p class="name">
<span class="first">Sungryong</span>
<span class="last">Kang</span>
</p>
...
new_name.attach($$('.name')[1]);
new_name.attached;
// [<p.name>]
동기화시키는 순간 Dom Element의 내용도 업데이트됩니다. 원래의 내용이 MooJoe 객체의 내용으로 덮어씌워집니다.
...
<p class="name">
<span class="first">Dachimawa</span>
<span class="last">Lee</span>
</p>
...

다시 뗄 때는 detach() 메서드를 사용합니다.
new_name.detach();
new_name.attached;
// []
MooJoe 0.9.0은 다음과 같이 체크아웃 받을 수 있습니다.
svn checkout http://moojoe.googlecode.com/svn/tags/0.9.0 MooJoe
개발버전을 받고싶으시면 다음을 사용해주세요.
svn checkout http://moojoe.googlecode.com/svn/trunk MooJoe
버그 리포트 및 기능 제안은 프로젝트 이슈 페이지에서 해주세요.
아직 버그가 있긴 하지만 앞으로 차근차근 발전시켜나가야겠습니다. 특히나 MooJoe 프로젝트는 저의 첫 개인프로젝트이니만큼 애정을 갖고 말이죠. 그럼 0.9.1 버전 릴리즈 소식도 기대해주세요!



