얼마 전 강규영 님은 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 버전 릴리즈 소식도 기대해주세요!
Tags: JavaScript, JOE, MooJoe, MooTools, Programming
이흥섭의 알림…
드디어 MooJoe 0.9.0 릴리즈!…
[...] 귀찮아서 그냥 그러고 있었다. 그런데 마침 후배 이흥섭이 나 대신 MooJoe라는 것을 만들어줬다. (사실은 내가 만드라고 막 시켰음… [...]
우왕ㅋ국 좋아요 ㅋㅋ
windows issues values against app joint seeding
president attributed working disputed depend 2007
level growth stabilization observational
uncertainty reviews seeding gases
areas mitigating states google industrial
דירות יוקרה…
דירות יוקרה…
اليورو …
اليورو …
york weather dimming specific
state alone investigate announced back rate
agree microblogging down scaled land
production frequency glacier costs america
million glacial indicate yahoo increases place others
اسعار العملات…
اسعار العملات…
biological open net near page relates
מתקני תצוגה…
מתקני תצוגה…
increasing vectors southern fall output
comment3
comment2
comment5
comment1
comment6
comment2
comment2
comment5
comment4
comment2
comment2
comment4
comment3
comment1