2007년 11월 06일
SWAF의 자바스크립트 OOP 지원
많은 자바스크립트 프레임웍들이 나름의 OOP를 지원하고 있습니다. 자바스크립트 어플리케이션이 대형화 되고, 복잡도가 높아지면서 OOP는 거스를 수 없는 추세이겠지요.
이에 SWAF도 여러가지 OOP 특성을 지원합니다. 특히, SWAF은 __proto__로 나타내어지는 prototype chain을 사용하지 않는 것이 장점이자 단점입니다.
__proto__를 사용할 경우, 결정적으로 IE에서 아직 지원되지 않기 때문에 실제 프로젝트에의 적용성이 떨어집니다. 혹 IE에서는 이를 시뮬레이션 한다 하더라도 그 성능이 최고가 되지는 않을 것이라고 생각합니다. 대신, __proto__를 사용하지 않음으로써 비표준적인 자바스크립트 상속 방법이 되었지만, 브라우져에 관계 없이 동일한 로직으로 수행될 수 있게 되었습니다.
IE가 이러한 자바스크립트 표준을 모두 지키게 될 때면, SWAF도 이를 이용하도록 업그레이드 해야 겠지요. 표준을 신봉하고, 표준을 지키는 것만이 올바른 개발이라 생각하시는 분들은 무척이나 욕하시겠지만, SWAF은 표준을 지키는 것 보다는 실제 프로젝트에서 가장 유용한 자바스크립트 프레임웍으로 만드는 것이 목표입니다. 때문에, 앞으로도 성능 및 실용성을 위해 어느 정도의 비표준은 감수할 예정입니다.
물론, 그렇다고 표준을 무시하거나 하는 것을 좋아하는 것은 아니지만, 항상 최신 표준을 적용하고 이를 준수하는 이상적인 프레임웍 보다는, 다소 시대에 뒤떨어지더라도 현업에서 유용하게 사용될 수 있는 프레임웍을 만들고 싶은 욕심입니다.
이야기가 다소 빗나갔는데, SWAF에서 제공하는 클래스의 주요 특징은 다음과 같습니다.
1. 상속
SWAF은 다중상속까지 지원합니다. 부모 클래스의 메소드가 overriding되며, 이 경우 부모 클래스의 원 메소드는 this.parent$method(...)와 같이 호출할 수 있습니다.
다중상속일 때 부모 클래스들의 메소드 이름이 서로 다르면 parent$method(...)로 호출하면 되지만, 메소드 이름이 같다면 class1$method(...), class2$method(...)와 같이 클래스명을 직접 명시할 수 있습니다.
Java나 C++에서 클래스의 생성자가 있듯이 SWAF에서도 init이라는 생성자 메소드가 있습니다. 다른 언어와 마찬가지로 상속된 클래스의 경우 생성자는 부모 클래스의 생성자를 먼저 호출하고 자신이 실행됩니다. 다중 상속의 경우에는 정의된 순서대로 부모 클래스의 생성자가 호출됩니다. 물론, 생성시 전달한 파라메터가 부모 클래스의 생성자에도 그대로 전달됩니다.
2. 이벤트 매핑
VC++의 MFC를 사용해 보신 분은 익숙한 방식일텐데, 클래스에서 이벤트에 대한 핸들러 매핑을 지정할 수 있습니다. 클래스 자체에서 이벤트 소스에 대한 DOM Selector 패턴을 지정할 수 있고, $event 함수를 통해 수동으로 바인딩 할 수 있습니다. 클래스에 이벤트 소스를 지정한다면, 클래스는 인스턴스 생성시 해당 소스에 자동으로 핸들러가 매핑됩니다.
Event쪽 설명에서 좀 더 자세히 다루겠지만, SWAF의 이벤트는 HTML 이벤트 뿐 아니라 사용자 정의 이벤트도 가능합니다. 그리고, 이것들에 대해 동일한 메커니즘으로 체인을 구성할 수 있습니다. 따라서, 클래스 간 메시징을 가능하게 합니다.
SWAF에서는 클래스 뿐 아니라 개체 정의 및 이용을 보다 편리하게 하기 위해 다음의 메소드를 제공합니다.
$extend(dest,src) - prototype.js의 extend와 같습니다. 단순히 src의 멤버를 dest에 복사하는 기능을 합니다.
$namespace(name,[define]) - fully-qualified object로 지정된 name이 존재하도록 보장합니다. 예를 들면, 아래와 같습니다.
$namespace('lib.myclasses');
이 경우 현재 window에 lib.myclasses object가 있는지 검사하고, 없으면 {}의 빈 object로 생성합니다. 만약, define을 지정하면 해당 개체는 define으로 생성됩니다. 즉,
$namespace('lib.myobjects',{ num : 0});
이렇게 한다면, lib.myobjects는 {num:0} 개체로 됩니다.
lib개체가 없다면, lib 개체도 생성됩니다. 만약, 지정 개체가 이미 존재하는 경우 아무것도 하지 않고 넘어갑니다.
SWAF에서의 클래스 정의는 다음과 같이 합니다.
$class({
classname : {
/* namespace*/
[NAMESPACE : namespace, ]
/* inheritance */
[BASE : class name, ]
[EXTEND : object name, ]
/* event handler mapping */
[EVENT_SRC : $pattern|object,]
[EVENT_MAP : {
event_name : handler(string),
event_name : handler(string),
...
},]
/* constructor */
[init : function (...) {
},]
/* method & property */
...
}
});
- NAMESPACE : 해당 클래스가 속할 개체를 지정합니다.
- BASE : 상속할 부모클래스를 지정합니다. 다중 상속의 경우 배열로 지정합니다.
- EXTEND : EXTEND가 지정될 경우 해당 개체의 모든 멤버를 현재 클래스에 덮어씁니다. 물론, 이 경우도 parent$를 통해 부모 개체의 멤버에 접근할 수 있습니다.
- EVENT_SRC : 이벤트 매핑에서 처리할 이벤트 소스를 지정합니다.
- EVENT_MAP : 이벤트 핸들러를 정의합니다.
이벤트 핸들러 매핑을 가지고 있는 클래스 정의에 대한 예를 들면 아래와 같습니다.
$namespace('lib.classes');
$class({
evtclass : {
NAMESPACE : lib.classes,
EVENT_SRC : 'evt_btn',
EVENT_MAP : {
click : 'onClick',
mouseover : 'onMouseOver',
mouseout : 'onMouseOut'
},
onClick : function(evt,src,type) {
alert('click');
},
onMouseOver : function(evt,src,type) {
src.innerHTML="OVER";
},
onMouseOut : function(evt,src,type) {
src.innerHTML="OUT";
}
}
});
위 클래스는 lib.classes 네임스페이스에서 정의되었기 때문에, 클래스 인스턴스 생성시 아래와 같이 합니다.
var cls = new lib.classes.evtclass;
인스턴스가 생성되면서, EVENT_SRC에 지정된대로 $('evt_btn')에서 검색된 개체가 EVENT_MAP에 정의된 핸들러로 매핑됩니다. 위 클래스의 인스턴스가 생성되면 evt_btn이라는 id를 가지고 있는 element를 클릭하면 'click' alert이 뜨게 됩니다. 마찬가지로, mouseover 및 mouseout 이벤트 발생시 innerHTML이 각각 OVER, OUT으로 변경됩니다.
또한, 후에 소개할 XML Context를 이용하면, factory pattern 및 single pattern이 구현되어 XML 정의만으로 클래스 인스턴스 생성 및 이벤트 매핑등이 되어 별도의 js 코드 없이 어플리케이션을 구현하는 것도 가능합니다.
다음은, 상속의 샘플 코드를 보겠습니다.
$namespace('lib.classes');
$class({
base : {
NAMESPACE : lib.classes,
EVENT_MAP : {
click : 'onClick'
},
init : function (msg) {
this.data1 += msg+' by base / ';
},
data1 : '',
onClick : function(evt,src,name) {
alert('click handler on base');
},
method1 : function(msg) {
return this.data + ':' + msg + ' by method1(base)\n';
}
}
});
$class({
childcls : {
NAMESPACE : lib.classes,
BASE : lib.classes.base,
EVENT_MAP : {
click : 'onClick'
},
init : function (msg) {
this.data1 += msg+' by child';
},
onClick : function(evt,src,name) {
alert('click handler on child');
this.parent$onClick(arguments);
},
method1 : function() {
return this.data1;
}
}
});
위 클래스의 인스턴스 생성은 다음과 같이 합니다.
var cls = new lib.classes.childcls('class');
위와 같이 인스턴스를 생성할 경우, 부모 클래스의 생성자도 자동으로 호출되기 때문에, cls.method1()을 호출할 경우 다음의 결과값이 리턴됩니다.
init by base / init by child
그리고, 아래와 같이 수동으로 이벤트 소스를 인스턴스에 매핑할 경우
$event($('evt_btn'),cls);
evt_btn이란 id를 가지는 개체에서 click 이벤트가 발생하면 childcls의 onClick이 호출되어 'click handler on child' 이란 alert이 표시됩니다. 그리고, 바로 this.parent$onClick(arguments); 을 통해 부모 클래스의 메소드가 호출되어 'click handler on base'이 alert으로 나타납니다.
마지막으로, 다중상속의 샘플코드를 살펴보겠습니다.
$namespace('lib.classes');
$class({
base1 : {
NAMESPACE : lib.classes,
init : function (msg) {
this.data1 += msg+' by base1\n';
},
method2 : function(msg) {
return msg + ' by method1(base1)\n';
}
}
});
$class({
base2 : {
NAMESPACE : lib.classes,
init : function (msg) {
this.data1 += msg+' by base2\n';
},
method2 : function(msg) {
return msg + ' by method1(base2)\n';
}
}
});
$class({
childcls2 : {
NAMESPACE : lib.classes,
BASE : [lib.classes.base1,lib.classes.base2],
init : function (msg) {
this.data1 += msg+' by child\n';
},
data1 : '',
method1 : function() {
return this.data1;
},
method2 : function(msg) {
return msg+' by child\n'+this.base1$method2(msg)+this.lib_classes_base2$method2(msg);
}
}
});
위 예에서 childcls2는 base1 및 base2 클래스를 차례로 상속받습니다. 이 때, 모든 클래스에 있는 method2는 overriding되어 childcls2의 method2가 됩니다.
생성자는 정의된 부모클래스로 차례로 호출되어, 아래와 같이 인스턴스 생성시
var cls = new lib.classes.childcls2('init');
cls의 data1은 'init by base1\ninit by base2\ninit by child\n'의 값을 갖게 됩니다.
다중상속을 받은 자식 클래스에서는 마찬가지로, parent$method를 통해 부모 클래스의 메소드를 호출할 수 있지만, 위의 예와 같이 여러 부모클래스에서 method 이름이 중복되는 경우 (위에서 method2) this.base1$method2(...) 또는, this.lib_classes_base2$method2(...)와 같이 부모 클래스를 명시하여 호출할 수 있습니다.
이상으로 swaf에서 제공하는 클래스의 기능에 대해 간단히 소개하였습니다.
아직은 부족한 기능들이 많지만, 좀 더 많은 의견을 수렴하고 연구하여 js에서도 클래스 모듈화 및 재사용을 가능하게 하고 제대로 된 OOP를 할 수 있도록 노력하겠습니다.
# by | 2007/11/06 16:28 | SWAF | 트랙백 | 덧글(0)




☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]