나름 정의하면 ajax는 Javascript로 XMLHttpRequest ActivX컨트롤을 통해서 서버와 비동기 통신으로 XML,JSON프로토콜,사용자지정포멧 등으로 데이타 주고받아 DHTML(XSLT)로 웹브라우저내에서 애플리케이션의 기능을 구현하는 기술을 총칭한다고 보면 되겠다. 더많은 ajax라는 용어에 대해 궁금하신 분은 -> http://ko.wikipedia.org/wiki/Ajax

 이 글은 위에서 정의한 ajax의 기술 중에 javascript의 확장된 기능(객체화)을 이용하여 클래스를 설계하는 방법에 대한 설명에 해당하므로 ajax의 포괄적인 의미와
충분히 구분을 가지기를 바란다.

 솔직히 필자도 자바스크립트를 많이 사용 했지만  function이 클래스 선언에 대응된다는 내용은 ajax라는 용어가 광범위하게 퍼진 후이고 객체화와 관련된 스크립트문법을 잘 모르는 부분이 많다. 그래서 본 사이트의 클래스들을 만들면서도 시행착오가 꽤나 많았고 조금 틀린 설명이 있더라도 양해 하시길 바란다.

javascript에 대한 간략한 문법을 먼저 설명 해 본다.
솔직히 script언어의 명세를 찾아볼 수도 있지만 특별히 많은 문법이 없어서 필자의 경우는 코딩을 하면다 이런저런 테스트를 다 해보면서 한다.
function Tab()
{
   var  member = "1";
}
으로 선언 하면 Tab이 하나의 클래스 선언에 해당한다? 라고 생각하지만 글을 읽어가면 아니라는것을 알게 될 것이다. 
뭐 기존의 컴파일러 언어들의 클래스하고 같다고 간단히 생각 할 수도 있지만 사정이 그렇지 못하다.
일반적으로 클래스 내에서 우리는 this라는 포인터를 사용한다. 왜? 이 클래스가 인스턴스화 되었을 때 자신의 포인터를 참조하기 위해서 일 것이다.(아님말구~)
하지만 자바스크립트는 컴파일러의 this처럼 function내에서 this를 확인 해 보면 현재 클래스가 아니라 아마도 window객체가 참조될 것이다.
초기에 필자는 (new Tab()).member하면 Tab의 멤버변수의 값인 1이 참조되는 줄 알았다.. 하지면 아니다 member는 전역변수가 된다. 아래의 예제와 함께 테스트 해보시라.

이거 참 거시기 같지 않은가? 이건 클래스가 아니고 window객체(클래스) 의 멤버함수이지 않은가? 멤버함수 맞다.
그렇다면 왜 클래스라고 하는가? 답은 클래스 처럼 new할 수 있기 때문이다. 한마디로 멤버함수 이면서 클래스 선언으로 사용 될 수 있게 설계를 해 놓은것으로 보면 되겠다. 함수 자체로 클래스정의를 하는것은 아님을 명심하자.

간단한 테스트를 위해 다음 코드와 주석을 보면,
<html>
<head>
<title>가이아타이틀</title>
<script>
function tab()
{
   //alert(this.document.title); //이 코드를 활성화 해보면 this가 window라는거 확인가능
  if(this == window)
     alert("dddd");
 return "1";
}
function initPage()
{
  alert(tab);        // 1) 오호라 놀라워라?
  alert(tab());     // 2) 누구나 아는 결과 "1"
  alert(new tab); // 3)좀 새로운 결과?

}
</script>
</head>

<body onload="initPage()">
<table>
<tr>
<td id="insObj"></td>
</tr>
</table>
</body>
</html>

1)의 결과는 무엇일까?
   난 처음에 object가 리턴될 줄 알았다. 하지만 그 결과는 놀랍다. tab함수의 선언에 해당하는 문자열이 리턴된다.
   한마디로 문자열이란 말인가? 필자도 모른다. 적어도 문자열은 아닌듯하다(간단히 다음코드로 테스트)
var strFun = "function test() { return 1; }";
function initPage()
{
  alert( strFun);
  alert( strFun()); //안먹는다, 당연하다. 단순한 변수에 ()를 붙여서 호출한다고 먹힐리가 없다.
}
결론적으로 alert(tab)을 하면 함수의 정의를 문자열로 뿌리기는 하지만 함수의 이름인 tab과 문자열과는 차이가 있다는 것이다.

2)의 경우는 누구나 아는 기본적인 함수 호출의 결과이다.
3)의 결과는 무엇일까?
   이 경우 초기에 필자는 그냥 object가 alert되는 줄 알았지만 결과는 좀 달랐다
   [object Object] 가 alert된다.
   호잉? 소문자 object, 대문자 Object ?? 도데체 이게 무슨 결과인가?
   처음 소문자 object는 알겠는대 그후의 Object는 도대체 무엇이란 말인가??
   혹시나 해서 alert(Object())라고 찍어본다. 어라..  또 object Object다? alert(new Object()) 와같이 new를 붙이면 어떤결과일까? 어라? 동일한결과??
   의혹이 증폭된다.. 도대체 Object라는 놈이 무엇이란 말인가??
   혹시나 싶어 alert(Object)를 찍어본다... 어라? function Object() { [native code] } 라는 결과가 나온다. 어라 이건 위에서 함수명만 찍었을 때와 비슷한 결과이다.
   음.. 결론을 내자면 잘 모르겠다..ㅎㅎㅎ 아시는분 댓글 부탁~~
   짐작을 하자면 new 함수명()으로 function을 new할 때 시스템 내장객체(함수) Object를 new해서 함수명에 해당하는 객체에 붙여버리는거 같다.
 
즉, 기존 함수객체는 window의 멤버함수이면서 클래스(객체)선언 이고 그 객체를 확장한다는 의미로 Object를 뒤에 붙여버리는듯 하다. 오호라 그럼 실제로 var tabObj =  new Tab()을하면 실재로 리턴되는것은 Object라는 것을 대충 짐작하게 될 것이다.
조금 달리말하면 객체의 형이 Tab인 default Object를 하나 만들어서 리턴해준다고 보면 되겠다.

실질적인 클래스(객체)정의
음... 그렇다면 실재적으로 new했을 때 리턴되는 Object형의 객체는 어떻게 사용하는 것인가????
누가 설계했는지 참 머리 좋다. 기존 단순한 함수기반 스크립트에서 객체로의 확장을 가능하게 설계를 한듯하다.
바로 prototype라는 키워드로 해당 함수가 객체화 되면 확장되는 Object를 채우게 될 객체의 명세를 정의(실질적인 클래스정의)하도록 해 놓은 것이다.
문법은 Tab.prototype = {,,,,} 이다.
참으로 간단하지만 참으로 강력한 문법이다.

 
괄호안은
멤버명 : 초기화값,
.... ,
멤버함수명 : function(파라메타,...)
{

},
.....
로 무한히 클래스 정의처럼 정의 해 가면 된다.
정의한 멤버함수 내에서 this.멤버명으로 값을 참조할 수 있고
var aTab = new Tab() // 실재로 Tab.prototype으로 정의된 녀석이 리턴된다는거 ~
aTab.멤버명 = 값
와 같이 값을 초기화 할 수도있다.

여기까지 이해 했다면 DHTML과 자바스크립트를 좀 다뤄보신 분이라면 어떤 DHTML컨트롤이라도 만들 수 있을 것이다.

본 사이트의 탭 구혀에 대한 본격적인 설명에 들어간다
1. 우선 탭을 구성하는 부분을 생각해 보자
   - 각 탭의 헤더(Header)와 바디(Body)가 한 쌍을 이룬다.(new Tab()했을 때 헤더도 생성하고 바디도 생성하면된다)
   - 각 헤더를 클릭하면 쌍에 해당하는 Body가 나타나고 나머지는 사라져야한다.(display속성이용)
2. 1번의 각 탭들을  담는 객체가 하나 필요하다.(TabGroup이라고 명명한다)
   - TabGroup은 각 탭들이 Add될때마다 배열에 각 탭객체를 담아서 관리한다.
   - 나중에 알게 되겠지만 각 탭이 클릭되었을 때 리턴될 콜백함수를 담을 변수도 빌요하다.
대충 이정도로 간단히 구현의 범위를 잡고 시작하면된다.

구현 1) 탭 객체를 정의한다
function Tab()
{
  ....//뭔가를 정의하고 싶을 것이다. 하지만 여기에 정의하는 변수들은 window전역 변수로 작동하므로 아무것도 정의 할 필요 없습니다.
}
또한 Tab(파라메타1,...)등으로 parameter를 넘겨서 뭔가를 초기화도 하고 싶겠지만 아~~~무 의미없습니다.
그냥 다음에 정의할 prototype을 정의하기 위한 중간 과정으로 보시면 됩니다.

//참고로 visual studio 2008로 javascript를 작성 시 한글 주석달면 IE6.0에서 한글이 깨지면서 자바스크립트 오류를 일으킬 수 있으니 참고하세요
  이거때문에 엄청 시간 낭비T.T
Tab.prototype =
{
   header : null, //contain header object
   body : null, // contain body object
   // 나중에 세부적인 변수는 추가 하고....
   CreateTab : function()
   {
      //탭의 헤더와 바디를 new해서 header,body에 저장
      //
   }
}
위와 같이하면 대충 Tab은 정의 된듯하고 그다음 각 탭을 생성해서 저장 할 TabGroup이 필요하다
function TabGroup(){}
TabGroup.prototype =
{
    count : 0,
    arrayTab : new Array(),
    clickedTab : null,
    callBack : null,
    frame : null,
   AddTab : function(sID,sName) //필요한 parameter넘긴다. 속성으로 지정할지 함수parameter로 넘길지는 편한대로 하면된다.
   {
     //실재로 new Tab()으로 생성해서 arrayTab에 추가하겠죠
   }
}
우선 기본 클래스 정의는 이렇게 하면된다.
이 구조는 일반적인 클래스 설계하는 방법과 같다. 단지 자바스크립트의 특수한 문법이 조금 가미되었을 뿐임을 명심하자.

JSON(JavaScript Object Notation)의 객체정의와의 비교
JSON의 객체정의법
 - 객체는 {} 으로 묶은 문자열로 표현,
 - 객체내의 멤버와 초기값은 멤버명 : 초기값 형태로 표현
    var jsonObject = {};
    //alert(jsonObject );
이 코드를 실행 해보라 객체가 alert된다. 어라? 위에서 함수를 new해서 찍는거랑 같네?
그렇다. 함수를 언하는것과  JSON표기법을 사용하는것과 동일하다는 결론이 나온다.
즉 객체를 함수선언으로 new해서 사용 할 수도 있고 JSON표기법을 이용할 수도 있다는 말이되겠다.
다른 표현으로 설명하면 함수는 객체의 형(클래스)선언이고 JSON표기법은 객체를 바로 기술하는 표기법이다.(그래서 new없이 바로사용)


다음코드를 보자.
var jsonObject =
 { 
  field1 : "field1",
  func1 : function()
  {
   alert("1222");
  }
 };
이런식으로 정의하여 사용하면 속성과 메소드를 포함하는 인스턴스 jsonObject가 만들어진다.
한마디로 위에서 함수정의 객체의 prototype를 이용하여 객체를 확정한 후 new해서 사용하는것과 똑같는 결론이다.

위 코드를 일반 문자열로 묶은경우 eval을 이용하여 JSON객체화 하여 사용 할 수도 있다.
    var jsonStr2 = "{var1 : '111', var2 : '222'}";
    alert(eval('(' + jsonStr2 +')') );
이렇게 앞뒤로 괄호를 붙여서 사용하면 OK,
이것은 웹서비스같은데서 리턴되는 문자열을 JSON형식으로 만들어 리턴한 후 객체화 하여 사용할 때 유용한 방법이다.

객체를 중첩해서 사용가능하다.
    var jsonStr1 = {var1 : '111', var2 : { var2_Sub : 'sub2' }};
    alert(jsonStr1.var2 ); // jsonStr1[0]식으로 배열인덱스로도 접근가능하다.

또한 .을 이용하여 추가적으로 확장해 갈 수도 있다.
var jsonStr1 = {};
jsonStr1.var1 = '111';
jsonStr1.var2 = {};
jsonStr1.var2.var2_Sub = 'sub2';
이런식으 코딩도 가능하지만 그렇게 보기좋은 모양은 아닌듯 하다.

그러면 jsonObject내부에 있는 function()에 대해서 한번 확인해보고 넘어가자.
func1 : function() 으로 정의 된부분은 일반 함수선언과 무엇이 다른가? 뭐 똑같지 않은냐? 라고 생각하겠지만 달랐다.

jsonObject.func1()식으로 일반함수처럼 사용하는것은 똑같다.
그러나 일반함수 선언은 new해서 인스턴스화가 가능했지만 이녀석은 인스턴스화가 되지 않는다.
var obj = new jsonObject.func1(); 하면 어떤 결과가 나올까?
필자는 obj가 func1()의 object객체가 될 줄 알았지만 결과는 아니다. 그냥 jsonObject.func()를 호출하는 것일 뿐이었다.
결론은 멤버함수일 뿐이지 새로운 형(클래스)선언은 아니었다.
(어찌보면 당연하다 이미 jsonObject자체가 메모리에 인스턴스로 올라온것이니까)

여기서 재미난 것이 있다.. 우리가 보통 함수포인터라고 부렸던 콜백기능에 대해서 다을 알 것이다.
자바스크립트에도 그런 기능을 구현 할 수 있다는것에 처음에는 정말 놀라웠다. 다음코드를 보자.
var jsonObject =
 {
  field1 : "field1",
  func1 : function()
  { 
        var Me = this;
        return function()
        {
             //if(this == Me)
             alert("1222");
        }
  }
 };
var funcPointer = jsonObject.func1(); // 이녀석이 C#의 델리게이트 선언에 해당한다고 보면된다.
funcPointer();

이코드를 실행해보면 어떤 결과가 나올까? 1222가 alert된다.
func1을 호출했을 때 function정의를 리턴하면 그 함수의 포인터??(아마도)가 리턴되고 변수에 담아놨다가 콜백함수 처럼 호출 할 수가 있는 것이다. 참으로 놀라운 결과이다.

위 코드에서 주석으로 처리된 부분 //if(this == Me) 을 주석부분을 풀면 1222가 호출되지 않는다는것도 중요하다.
Me에 저장된 this포인터는 jsonObject이고 return function()내부에서의 this는 새로운 함수의 포인터라는 차이점이 있다.
콜백 function내부에서 jsonObject의 포인터를 사용하기 위해서는 위와같은 형식으로 Me변수를 정의하여 내부에서 사용해야함을 잊지말아야 할것이다.

그런데 함수포인터를 어떻게  사용하는 것일까?
C#의 delegate를 사용해 보신분들은 해당 event delegate에 함수를 +연산으로 add하는 것을 알고 있을 것이다.
이녀석도 똑 같다. 리턴된 함수포인터는 임의의 함수 형을 담을 수 있는 녀석이다.
다음과 같이 다른 임의의 함수를 대입만 하면 해당함수를 호출하게된다.
function funcCallBack() { alert('aaaa'); }
var jsonObject =
 {
  field1 : "field1",
  func1 : function()
  { 
        var Me = this;
        return function()
        {
             //if(this == Me)
             alert("1222");
        }
  }
 };
var funcPointer = jsonObject.func1();
funcPointer = funcCallBack ;
funcPointer();

음... 그렇다면 
      return function()
        {
             //if(this == Me)
             alert("1222");
        }
부분에 함수를 파라미터로 넘겨서  코딩을 해두면 어떠한가?
function funcCallBack() { alert('aaaa'); }
function initPage()
{
jsonObject =
 {
  field1 : "field1",
  func1 : function(callBack)
  { 
   var Me = this;
   return function()
   {
         //if(this == Me)
        callBack();
   }
  }
 };

    var test = jsonObject.func1(funcCallBack);
    test();
}
이런 형태의 코딩이 나오게 된다. 함수를 파라미터로 넘겨놓고 필요한 경우에 콜백이 되게 호출 할 수 있는 구조가 가능하다.
여기까지를 이해하면 콜백함수를 구현하는 방식은 다 이해된듯하다.
이제 또 중요한 것이 있다. 해당 콜백함수에 jsonObject의 포인터를 넘겨서 어느객체가 콜백을 호출했느냐를 판단을 해야 하는 경우가 있다.
그때 사용하는것이 apply함수이다.
함수가 호출될 때 호출객체를 바꿔치기 해주는일과  호출 파라미터를 배열형태로 넘기게 해준다는 정도만 알 뿐이다.
최종 콜백구현은 아래의 코드와 같다.
function funcCallBack(param1)

  // 여기에서 this포인터를 참조하게되면 콜백의 호출인 경우 아래 jsonObject의 포인터가된다.
  alert(param1);
}
function initPage()
{
jsonObject =
 {
  field1 : "field1",
  func1 : function(callBack)
  {
                  var Me = this;
   return function()
   {
      var arrArg = new Array();
      arrArg[0] = 'aaa';
      callBack.apply(Me, arrArg);

      if(this == Me)  //this point check
       alert("xxxxx");
   }
  }
 };

    var test = jsonObject.func1(funcCallBack);
    test();
}
여기까지의 내용을 이해했다면 당신은 이제 자바스크립트의 재미에 흠뻑빠져든 것이다.
물론 HTML DOM구조등 기존 자바스크립트 사용법은 다 이해하고 있다는 가정하에서이다.
하지만 스크립트 언어는 한계가 있다. 실재로 객체지향적으로 구현을 해 보면 느끼겠지만 제대로 작동하지 않는 속성이나 메소드도 있다.
객체가 중첩되고 중첩될 수록 그런경우는 더 많아지는것 같다. 물론 필자가 캐치하지 버그일 수도 있지만... 그런 버그를 많이 양산 한다는 것 또한 맘에 들지 않는 부분이다. 주저리주저리.....





Posted by Sting!
,