블로그 이미지
초딩입맛제주아재
하고 싶은 것만 하며 살고 싶다

calendar

1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
2007. 6. 14. 17:07 Programing/HTML/JavaScript/CSS

Node.cloneNode(deep of type boolean)


이름에서 느껴지듯 cloneNode는 해당 노드를 복제하는 메서드다.

아래와 같은 HTML 엘리먼트들이 있다.

전체 과자


과자를 클릭하면 셀렉트박스에는 과자에 해당하는 아이템들이 등록되고
빵을 클릭하면 빵에 해당하는 아이템들이 등록되고, 전체를 클릭하면 셀렉트박스의 모든 아이템을 삭제해야 한다.

아주 간단한 작업이다.

var snack = new Array('깡좋은새우','자갈치시장','고래식사','인디언식사');
var bread = new Array('크림빵','단팥빵','식빵','바게트');


이렇게 배열로 미리 아이템들을 만들어 두고 클릭이벤트 핸들러에서 타겟에 해당하는 배열의 원소들을 OPTION객체로 만들어서 셀렉트박스에 밀어 넣어 주면 된다.

function putItem(selObj,arr){
    for(var i=0,max=arr.length; i<max; i++){
        var option = document.createElement('OPTION')
              option.value = arr[i];
              option.text = arr[i];

        selObj.add(option);
    }
}

쉽다...very easy....

그러면 위와 같이 배열을 만들지 않고 이미 만들어진 셀렉트박스의 내용을 참조하여 다른 셀렉트 박스에 똑같은 아이템을 추가해야 한다면 어떤가....

아래와 같은 상황이라면 말이다..

  

경우에 따라서 빵과 과자에 해당하는 아이템들을 타겟 셀렉트박스에 넣어야 한다면 어떤 방법을 써야 할까.
원본의 아이템을 그대로 복사해서 타겟에 추가하면 된다.

function putItem(src,target){
    //target 셀렉트박스의 아이템을 초기화(삭제)하는것은 생략한다.

    for(var i=0,max=src.length; i<max; i++){
       var newopt = document.createElement('OPTION');

       newopt.value = src.options[i].value;
       newopt.text = src.options[i].text;

       target.add(newopt);
    }
}

하지만 이 방법은 새로이 OPTION객체를 만들어야 하기때문에 value와 text를 지정하는 부분까지 덤으로 추가가 되는 단점이 있다.

이럴때 cloneNode를 쓰면 간편해진다.

function putItemNew(src,target){
    var temp = src.cloneNode(true);

    while(temp.length > 0){
       target.appendChild(temp[0]);
    }
}

원본 셀렉트 박스를 복제하여 그 원소를 타겟으로 이동시키는 방법이다.

그런데 while 블럭 안을 보고 좀 의아하다는 생각을 갖게 될 수 있다.
블럭 어디에도 temp의 childNode를 삭제하는 코드가 없는데 계속 제일 처음 원소를 타겟에 추가하고 있다.
게다가 length가 저절로 줄어서 반복문이 종료 되었다.

이유는 바로 appendChild 때문이다.
appendChild 는 해당 노드에 인자로 받은 노드를 추가 하는 메서드이다.
A에 있던 노드를 B에 추가하면 어떻게 되겠는가?
복제하여 추가하지 않는 이상 A에 있던 노드를 B로 이동시켜야 한다.
그러니 자연히 A에서는 노드가 줄어들게 되는 것이다.

위의 예제에서는 겨우 두세줄의 코드를 줄이는 것에 불과하였지만 속성이 많이 달려있는 엘리먼트의 경우를 상상하면 cloneNode의 유용함을 부인 할 수 없다.

posted by 초딩입맛제주아재
2007. 5. 31. 18:41 Programing/HTML/JavaScript/CSS

[Flash] http://asrada2001.mireene.com/asrada/test/수정이가능한텍스트예제2.swf



인라인텍스트에디터를 응용하여 가계부에 기능을 추가했다.
selectabeText.js 파일을 만들어서 editableText 와 비슷하게 코드를 작성했다.
각각의 에디터에서 호출하는 callback는 동일하기 때문에 객체의 인스턴스변수를 비슷하게 구성해야했다.

둘 다 메서드 구성이나 변수 구성이 비슷한데 왜 메서드 이름은 통일하지 않았는지 ㅡㅡ;
이름짓는 일이 제일 어려운것 같다..

이렇게 radioableText.js, checkableText.js 등을 늘려간다면 중복되는 메서드들이 산재하게 될것이다.
Editor 라는 인터페이스를 만들어야겠다.


interface : Editor
==================
container
source
value
isChanged
callback
autosave
------------------
setModel
editing
complete
save
cancel
finish
==================

posted by 초딩입맛제주아재
2007. 5. 29. 23:21 Programing/HTML/JavaScript/CSS
가계부를 업데이트 하면서 적용해봤는데 나름 만족할만한 정도다.

[Flash] http://asrada2001.mireene.com/asrada/test/수정이가능한텍스트예제.swf



테이블 셀 작업 샘플보기

[editableText.js]
/*
작성자: 물결(asrada2001.mireene.com)
작성일: 2007-05-29
*/

function createEditor(callback){
   var editor = new Editor();
   editor.callback = callback;

   try{
       document.addEventListener('click',clickEventHandler,false);
   }catch(e){
       document.attachEvent('onclick',clickEventHandler);
   }

   return editor;
}
function clickEventHandler(e){
   var evt = e || window.event;
   var target;

   if(evt.target){
       target = evt.target;
   }else{
       target = evt.srcElement;
   }

   if(target.className.search('editable') > -1){
       editor.editing(target);
   }
}

function Editor(){
   this.autosave = false;
   this.source = null;
   this.callback = null;
   this.container = null;
   this.frm = document.createElement('INPUT');
   this.frm.className = 'editor';
   this.frm.model = this;

   this.frm.onkeydown = function(e){
       var evt = e || window.event;

       if(evt.keyCode == 13){
           try{
               evt.preventDefault();
           }catch(ex){
               evt.returnValue = false;
           }

           this.model.complete();
       }
   }

   this.frm.onblur = function(e){
       if(this.model.autosave == true){
            this.model.complete();
        }else{
            this.model.cancel();
        }
   }
}

Editor.prototype.editing = function(obj){
   try{
       this.container = obj;

       if(obj.hasChildNodes() == false){
           obj.appendChild(document.createTextNode(''));
       }

       this.source = obj.firstChild;
       this.frm.value = this.source.nodeValue;
       this.frm.style.width = '100%';

       if(this.container.style.textAlign != undefined && this.container.style.textAlign != ''){
            this.frm.style.textAlign = this.container.style.textAlign;
        }else if(this.container.align != undefined && this.container.align != ''){
            this.frm.style.textAlign = this.container.align;
        }else{
            this.frm.style.textAlign = 'left';
       }

       this.container.replaceChild(this.frm,this.source);
       this.frm.focus();
       this.frm.value += '';
   }catch(e){
   }
}

Editor.prototype.cancel = function(){
   this.finish();
}

Editor.prototype.complete = function(){
   this.save();
   this.finish();

   if(this.callback instanceof Function){
       this.callback(this.container);
   }else{
   }
}

Editor.prototype.finish = function(){
   this.container.replaceChild(this.source,this.frm);
}

Editor.prototype.save = function(){
   this.source = document.createTextNode(this.frm.value);
}


모듈을 페이지에 로드시키고 에디터 객체를 생성하면 준비는 끝.
<script type="text/javascript" src="editableText.js"></script>
<script type="text/javascript">
var editor = createEditor(callback);

function callback(obj){
}
</script>


사용자가 문서의 텍스트 부분을 클릭하면 텍스트 노드를 담고있는 container 객체의 className 속성에 'editable' 가 있는지 확인한 후 에디터 객체(INPUT)에 텍스트를 전달하고 텍스트노드와 에디터를 바꿔치기한다.

에디터는 싱글턴(singleton)으로 하나의 에디터가 문서 전역에서 동작한다.

autosave 속성이 true로 설정되면 에디터에서 수정후 포커스가 다른 곳으로 이동하면 자동으로 수정한 내용이 원본 텍스트 노드에 그대로 적용이 되고 반대로 false면 원 상태로 되돌린다.
autosave를 적용하지 않으면 사용자는 데이터 수정 후 엔터키를 눌러야 수정한 내용이 반영된다.

callback 함수를 지정하면 save() 메서드를 실행 후 callback 함수를 호출한다.
xmlHttpRequest 객체를 사용할 경우 동적으로 DB의 내용을 수정 할 수 있다.

원래는 source를 <span>태그에만 할당 하려했으나 셀작업에 쓰는게 더 좋을 것 같아서 'edtiable'을 className으로 갖는 모든 객체로 수정하였다.
하지만 변경 후 맘에 안드는 것이, 원글 너비(offsetWidth)의 반영이다.
<span>,<div>등 block 객체에 edtiable 을 적용하면 원글의 사이즈에 맞게 에디터의 사이즈를 조절할 수 있는 반면 테이블의 셀(TD)에 적용 할 경우 테이블 셀 크기에 맞춰버리니 맞춤 사이즈는 포기할 수 밖에 없다....
방법을 아시는 분은 코멘트를 부탁...^^;


*주의*
Editor는 멀티라인 편집을 지원하지 않는다.
<span>,<td>,<div>,<p> 의 엘리먼트에서 오직 가장 처음에 위치한 텍스트 노드를 편집 할 수 있다.
멀티라인 편집을 위해 Editor 객체에 에디터를 input과 textarea 두개로 구분하여 childNodes.lenth로 멀티라인
여부를 판별한 후 해당 엘리먼트에 맞는 에디터를 쓰게끔 리펙토링을 시도하였으나,
editing() 메서드와 finish() 메서드에서 replaceChild를 쓸 수 없고,
텍스트 노드 사이의 태그를 검출해야 하며, 멀티라인의 경우 줄바꿈 처리를 하는 등 코드가 복잡해지는 관계로 단문 인라인 엘리먼트에 이 스크립트를 사용할 것을 당부하는 것으로 대신한다.

'Programing > HTML/JavaScript/CSS' 카테고리의 다른 글

cloneNode 의 활용  (0) 2007.06.14
수정이 가능한 텍스트2 - 인라인셀렉트박스??  (0) 2007.05.31
Javascript 객체 들여다보기  (0) 2007.05.22
What is the Flapjax.  (0) 2007.04.19
Switching Tab  (1) 2007.04.18
posted by 초딩입맛제주아재
2007. 5. 22. 14:49 Programing/HTML/JavaScript/CSS
javascript의 객체의 속성은 객체.속성이름 으로  접근이 가능하다.
그리고 또 하나 다른 표현이 있는데 바로 객체['속성이름'] 이 그것이다.

var obj = new Object();
obj.id = 'first';
obj.name = 'wave';
obj.age = '20';
obj.sex = 'male';

alert(obj.id);
alert(obj['id']);

이제 객체 안을 통째로 들여다 보는 방법을 알아보자
위에서 언급한 객체['속성이름']의 표현식을 이용하면 된다.

var msg = '';

for(var temp in obj){
  msg += temp + ' = ' + obj[temp] + '\n';
}

alert(msg);

for(A in B)는 B의 속성을 하나하나 꺼내서 A에 담는다.

어찌 보면 객체 == 배열 의 형태로 보이기도 하는데, phpschool 에 이와 관련한 재밌는 토론이 있었으니 참고하길 바란다...토론보기

posted by 초딩입맛제주아재
2007. 4. 19. 11:43 Programing/HTML/JavaScript/CSS

아프리카 초원의 날씨만큼이나 변덕스러운 웹 세상에 또 하나의 주목할 만한 언어가 등장했다.
이름하여 Flapjax.
웬지 이름에서 AJAX의 냄세가 느껴지지 않는가?

Flapjax 홈페이지에서는 Flapjax를 다음과 같이 소개하고 있다.

Flapjax is a new programming language designed around the demands of modern, client-based Web applications. Its principal features include:

  1. Event-driven, reactive evaluation
    print time of last click of btn1 or btn2 time: {! merge($EVENT('btn1', 'click'), $EVENT('btn2', 'click')).
    snapshot((new Date()).getTime()) !}
  2. Persistent data saved on a data store we provide
    keep a copy on the server of 'age' as it changes writePersistentObject($E('age'), {path: ['savedAge']});
  3. Convenient data sharing
    create a link to share the current user's tasklist getSharingConsoleLink('receivepage.html?user=' + uidB, ['tasklist']);
  4. Access-control for shared data
    display whether the user can write to an object {! readPermissionsB({path:['tasklist']}).has('WRITE')?'yes':'no'!}
  5. Interfaces to external Web services
    show the first link of a user's delicious bookmarks <input type=text id=u value='enter name'/>:
    {! getWebServiceObject_e({url: 'del.icio.us/feeds/json/'
    + $B('u'), serviceType: 'jsonLiteral'}).posts[0].d !}
  6. Optional templating syntax
    provide a live preview of a form value <input type=text id=age/>
    preview: {! 'age:' + $B('age') !}

홈페이지에서는 Flapjax 를 새로운 언어로 규정하고 있는데 실제로 Flapjax 코드를 보면 전혀 낯설지가 않다.
그 이뉴는 Flapjax가 Javascript를 확장한 것이기 때문인데 다음 예제를 보면 무슨말인지 이해가 갈 것이다.

<html>
  <head>
  <script lang="text/javascript" src="/demos/flapjax.js">
  </script>
  <script lang="text/javascript">
     var loader = [];
     var flapjax = flapjaxInit(false, false);
  </script>
  <title>
     Flapjax Demo: Where's the Mouse?: Flapjax Scripting
  </title>
 
  <script lang="text/javascript" type="">
     function loader0()
     {flapjax.insertDomB(flapjax.mouseTop_b(document), "Mtop");
      flapjax.insertDomB(flapjax.mouseLeft_b(document), "Mleft");}
     loader.push(loader0);
  </script>
 
  </head>
 
  <body onload="flapjax.forEach(function(l){ l(); }, loader)">
  <p>
     The mouse's coordinates are
&lt;
     <span id="Mleft">
       _
     </span>
     ,
 
     <span id="Mtop">
       _
     </span>
     &gt;.

  </p>
 
  </body>
 
</html>

위 예제는 마우스 포인터의 위치를 화면에 표시해주는 코드이다.
기존의 Javascript 코드와 전혀 다를바가 없다.

하지만, 사실 이 코드는 컴파일된 코드이다.
갑자기 무슨 컴파일이 나오는지 당황하겠지만 Flapjax는 Flapjax API를 이용해 코드를 작성하고  컴파일러를 통해서 아웃풋을 만들어야 브라우져에서 인식할 수 있다.
위 코드의 실제 Flapjax 코드는 아래와 같다.

<html>
<head>
<title>Flapjax Demo: Where's the Mouse?: Flapjax Scripting</title>

<script lang="flapjax">
insertDomB(mouseTop_b(document), 'Mtop');
insertDomB(mouseLeft_b(document), 'Mleft');
</script>

</head>

<body>
<p>
The mouse's coordinates are
&lt;<span id="Mleft">_</span>,
  <span id="Mtop">_</span>&gt;.
</p>
</body>

</html>


이처럼 Flapjax를 이용하면 개발자가 작성할 코드가 줄어들게 되는데 Flapjax측은 그 양을 아웃풋의 두배정도까지 줄일 수 있다고 한다.

단순히 코드의 양을 줄이는것이 Flapjax의 전부라면 Flapjax에 의미를 둘 수는 없다.
Cameron Laird은 IBM 개발자 네트워크에 포스팅한 Flapjax 튜토리얼에서 Flapjax를 특징을 아래와 같이 설명하고 있다.

Flapjax를 배워야 하는 이유

Flapjax는 Ajax를 손쉽게 코딩할 수 있는 방법을 제공하지만 Ajax를 편리하게 쓸 수 있는 래퍼 그 이상이다. 다음 섹션에서는 Flapjax에 대해 좀더 자세히 다루고자 한다. Flapjax에 대한 전체 내용을 다루기에는 본 튜토리얼의 목적에 벗어나는 감이 있어 일부만 소개한다. 본 기술문서에서는 다음 내용을 비롯하여 Ajax 프로그래밍 기술을 단순화하는 데 Flapjax가 어떤 역할을 담당하는지 보여준다.

  • 브라우저의 "새로 고침" 업데이트 관리
  • 사용자의 포인트 앤 클릭에 대한 응답
  • 복잡한 웹 애플리케이션을 위한 네트워크 로드 최적화
  • "외부" 서버 정보 검색

Flapjax는 입력(예: 최종 사용자의 동작과 선택)과 출력(예: 브라우저 창에서 보기) 간의 원활한 정보 교환, 즉 "반응"을 정의하고 표현하는 데 사용된다. 동시에 Flapjax는 기존 자바스크립트 작업과 호환 가능하다. Flapjax에는 자바스크립트에서 이미 쓰이고 있는 DHTML(Dynamic HTML)과 CSS(Cascading Style Sheets)가 사용되며, 현재 어떠한 새로운 시각적 요소도 추가되지 않았다. 이러한 관점에서 Flapjax는 순수하게 알고리즘 방식을 사용하고 "시각적"인 어떠한 것도 제공하지 않으며 이미 알고 있는 위젯을 더 쉽게 프로그래밍할 수 있도록 도와준다.


갈수록 AJAX 애플리케이션의 개발이 활발하게 이루어지고 있다.
더이상 AJAX는 고급 기술자들만의 전유물이 아니다.
다양한 프레임워크와 손쉽게 원하는 아웃풋을 만들어주는 언어와 툴들이 하루가 멀다하고 발표되고 있다.
Flapjax도 그 중 하나로, 앞으로 얼마만큼 개발자들의 호응을 얻으며 발전해 나갈지는 미지수다.
하지만 분명한것은 Flapjax는 배우기 어려운 언어도 아니며 사용하기 까다로운 언어도 아니라는 것이다.
그리고 그점은 분명 메리트로서 작용할 것이다.


posted by 초딩입맛제주아재
2007. 4. 18. 15:08 Programing/HTML/JavaScript/CSS
이 포스팅에서 '탭'이라함은 파이어폭스나 IE7에서 쓰이는 탭브라우징의 그 '탭'과 같은 의미이다.
다음 그림을 보면 쉽게 이해가 갈 것이다.

탭이란

Ariticles,Location log,Key Log 같은 것들이

컨텐츠가 표시되는 영역은 모든 탭이 똑 같다.
여러 컨턴츠 레이어들이 겹쳐져 있는 상태에서 선택된 탭에 해당하는 컨텐츠만 사용자에게 보여주는 것이다.

보통 이 탭기능을 웹에 표현하는 방법으로 이미지를 주로 쓴다.
이미지로 표현하는것이 미관상 좋고 만들기도 수월하기 때문인데, CSS 와 javascript 로 구현하는것도 가능하다.
심플한 텍스트 기반 페이지에 탭기능을 추가하고 싶다면 주목!

다음과 같은 탭을 만들어 보겠다.
오늘 이걸 하느라 아침 시간을 다 보냈다....내공부족...
사용자 삽입 이미지

tab1 ~ tab3 까지 총 세개의 탭은 각각 content1 ~ content3 까지 자기만의 컨텐츠를 갖고 있다.
tab을 누르면 활성화된 탭과 컨텐츠 레이어는 배경색이 흰색으로 바뀌고 활성상태에서 비활성 상태로 전환되는 탭은 배경색이 회색으로 바뀐다.

코드를 살펴보도록 하겠다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title> Switching Tab </title>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<script type="text/javascript">
<!--
var model = new Array();

function init(){
}
-->
</script>
<style>
.tab{
   width: 50px;
   height: 20px;
   border-top: solid 1px #D9D9D9;
   border-right: solid 1px #D9D9D9;
   border-left: solid 1px #D9D9D9;
   background-color: #EEEEEE;
   position: absolute;
   z-index: 2;
}

#tab1{
   margin-left: 10px;
}

#tab2{
   margin-left: 60px;
}

#tab3{
   margin-left: 110px;
}

.content{
   width: 200px;
   height: 50px;
   border: solid 1px #D9D9D9;
   background-color: #EEEEEE;
   position: absolute;
   top: 19px;
   z-index: 1;
}

#layer1{
   top: 50px;
   left: 50px;
   position: absolute;
}

#layer2{
   top: 50px;
   left: 50px;
   position: absolute;
}

#layer3{
   top: 50px;
   left: 50px;
   position: absolute;
</style>
</head>

<body onload="init()">

<div id="layer1">
   <div id="tab1" class="tab">tab1</div>
   <div class="content">content 1</div>
</div>

<div id="layer2">
   <div id="tab2" class="tab">tab2</div>
   <div class="content">content 2</div>
</div>

<div id="layer3">
   <div id="tab3" class="tab">tab3</div>
   <div class="content">content 3</div>
</div>

</body>
</html>

model은 레이어의 레퍼런스를 담을 배열 객체이다.
매번 각 레이어 객체를 찾아야 하는 불편을 덜기 위해 사용했다.

이제 본격적으로 javascript를 구현해보자.
onload 시에 호출 되는 init() 함수와 그에 따른 몇개의 함수가 필요하다.

function init(){
   var layer1 = document.getElementById('layer1');
   var layer2 = document.getElementById('layer2');
   var layer3 = document.getElementById('layer3');

   model.push(layer1);
   model.push(layer2);
   model.push(layer3);

   for(var i=0,max=model.length; i<max; i++){
       set_event_listener(model[i],'mouseover','switching_tab');
   }

   switch_execute(layer1);
}
init()함수는 각 레이어의 레퍼런스를 배열 model 에 추가하고 이벤트를 걸어준 다음 첫번째 레이어를 활성화 시킨다.


function set_event_listener(obj,evt_type,callback){
   if(window.event){
       obj.attachEvent('on' + evt_type,eval(callback));
   }else{
       obj.addEventListener(evt_type,eval(callback),false);
   }
}
set_event_listener() 함수는 주어진 객체에 evt_type 형식의 이벤트를 걸어준다.


function switching_tab(e){
   var target;

   if(window.event){
       target = window.event.srcElement;
   }else{
       target = e.target;
   }

   switch_execute(target.parentNode);
}
switching_tab() 함수는 이벤트가 발생할때 호출 되며 이벤트가 일어난 객체를 switch_execute 로 보내는 역할을 한다.



function switch_execute(layer){
   var visible_tab_top = -1;
   var hidden_tab_top = 0;

   if(window.navigator.appName.indexOf('Explorer') > -1){
       visible_tab_top++;
       hidden_tab_top++;
   }


   for(var i=0,max=model.length; i<max; i++){
       var tab = get_tab_element(model[i]);
       var content = get_content_element(model[i]);

       if(model[i] == layer){
           model[i].style.zIndex = 99;
           tab.style.top = visible_tab_top;
           tab.style.backgroundColor = '#FFFFFF';
           content.style.backgroundColor = '#FFFFFF';
       }else{
           model[i].style.zIndex = 0;
           tab.style.top = hidden_tab_top;
           tab.style.backgroundColor = '#EEEEEE';
           content.style.backgroundColor = '#EEEEEE';
       }
   }
}
switch_execute() 함수는 주어진 레이어를 활성화 시키고 나머지 레이어를 비활성화 시키는 일을 한다.
visible_tab_top과 hidden_tab_top 값을 브라우져에 따라 달리한 이유는 IE와 Mozila 계열의 렌더링 방식의 차이때문이다.
기본값은 FF를 위한 값이다.
만약 위와 같은 처리를 생략했을 경우 IE에서 는 활성화 된 탭과 그에 해당하는 컨텐츠 레이어 사이의 경계선이 그대로 남게 되고 FF에서는 탭의 좌우 경계선이 컨텐츠 레이어 안으로 비집고 들어오는 모습을 보게 될 것이다.


function get_tab_element(model){
   var child = model.childNodes;

   for(var i=0,max=child.length; i<max; i++){
       if(child[i].nodeType == 1 && child[i].className == 'tab'){
           return child[i];
       }
   }
}

function get_content_element(model){
   var child = model.childNodes;

   for(var i=0,max=child.length; i<max; i++){
       if(child[i].nodeType == 1 && child[i].className == 'content'){
           return child[i];
       }
   }
}
위 두 함수는 레이어에서 탭과 컨텐츠 영역을 추출하는 용도로 쓰인다.
역시 IE와 FF의 차이가 있기 때문에 firstChild, lastChild 를 쓰지 않았다.
속도가 느려지는 일이 발생한다면 위 두함수를 하나로 묶어서 반복문을 하나로 축약해야 할 것이다.
별로 그럴일은 없을것 같지만...

posted by 초딩입맛제주아재
2007. 4. 6. 13:59 Programing/HTML/JavaScript/CSS
자바스크립트는 참 어렵다.
쉬운것 같으면서도 어렵다.
그 이유가 바로 이 글에 나와있다.

===================================================================================

JavaScript는 LiveScript라고 불렸어야 되지 않았나 생각됩니다.
  JavaScript는 Java와는 아무 관계가 없으며,
  Sun의 공동설립자 중 한명인 Bill Joy는 Netscape가 JavaScript라는
  이름을 사용할 수 있도록 허용한 것을 후회한다고 밝힌적이 있습니다.- onjo
http://www.zdnet.co.kr/news/internet/etc/0,39031281,39149821,00.htm
http://www.elancer.co.kr/eTimes/page/eTimes_view.html?str=c2VsdW5vPTQ5ODQ=

자바스크립트 추천서적
  JavaScript: The Definitive Guide, David Flanagan, O'REILLY

--------------

원저 : <JavaScript: The World's Most Misunderstood Programming Language>
http://javascript.crockford.com/javascript.html

번역 : <JavaScript: 세상에서 가장 오해가 많은 프로그래밍 언어>
http://home.postech.ac.kr/%7Eskyul/javascript.html

번역자 : 서광열 http://skyul.tistory.com/172


JavaScript:
세상에서 가장 오해가 많은 프로그래밍 언어

Douglas Crockford
www.crockford.com

번역: Kwang Yul Seo
skyul.tistory.com

Mocha, LiveScript, JScript, ECMAScript 등으로도 불리는 JavaScript는 세상에서 가장 유명한 프로그래밍 언어 중에 하나입니다. 사실상 세상의 모든 개인 컴퓨터에 최소한 하나 이상의 JavaScript 인터프리터가 설치되어 있고 활발하게 사용되고 있습니다. 이러한 JavaScript는 인지도는 전적으로 월드와이드웹(WWW)의 스크립트 언어라는 역할 덕택입니다.

이런 인지도에도 불구하고, JavaScript가 매우 동적이며 객체지향의 범용 프로그래밍 언어라는 사실을 아는 이는 드뭅니다. 이런 사실이 어떻게 비밀이 될 수 있었을까요? 왜 이 프로그래밍 언어는 잘못 이해된 것일까요?
이름

Java- 접두사를 보면 JavaScript가 Java와 어떤 식으로는 연관 관계가 있을 것처럼 보입니다. 즉, JavaScript는 Java의 서브셋이거나 기능이 부족한 버전의 Java라는 인상을 줍니다. 이 이름은 혼란을 주기 위해 의도적으로 지어진 것으로 보이는데, 이런 오해가 혼란을 야기했습니다. JavaScript는 인터프리트되는 자바가 아닙니다. 자바 자체가 인터프리트되는 언어입니다. JavaScript는 Java와는 다른 언어입니다.

Java가 C와 문법적으로 유사하듯이 JavaScript는 Java와 유사합니다. 하지만 Java가 C의 서브셋이 아닌 것처럼 JavaScript도 Java의 서브셋이 아닙니다. JavaScript는 Java(초기의 Oak)과 원래 의도했던 응용 프로그램 부분에서는 Java보다 더 뛰어납니다.

JavaScript는 Java의 고향인 썬마이크로시스템즈(Sun Microsystems)에서 개발된 것이 아닙니다. JavaScript는 네스케이프(Netscape)에서 개발되었습니다. 원래는 LiveScript라고 불렸는데, 이 이름은 그다지 혼란스럽지 않았지요.

-Script 접미사는 JavaScript가 실제 프로그래밍 언어가 아니라 프로그래밍 언어보다 약한 스크립트 언어라는 인상을 줍니다. 하지만 이는 전문화의 문제입니다. C 언어와 비교해 보면, JavaScript는 표현력과 역동성(dynamism)을 위해 성능을 희생한 것입니다.
C 언어의 옷을 입은 Lisp

JavaScript의 C 언어 같은 문법(중괄호와 투박한 for 문을 포함)은 JavaScript가 일반적인 프로시저형 언어로 보이게 합니다. 이는 잘못된 오해인데, 왜냐하면 JavaScript는 C나 Java보다는 Lisp or Scheme와 같은 함수형 언어와 더 유사점이 많기 때문입니다. JavaScript는 리스트 대신에 배열이 있고, 속성 리스트(역주: 일종의 해시테이블) 대신에 오브젝트가 있습니다. 함수가 제1클래스이고, 클로저도 있습니다. 또한 괄호를 맞출 필요 없이 람다(lamda, 익명 함수)를 사용할 수도 있습니다.
고정역할

JavaScript는 네스케이프 네비게이터에서 동작하도록 설계되었습니다. 네스케이프에서의 성공은 JavaScript가 거의 모든 웹브라우저에서 표준 장치가 되도록 만들었습니다. 이는 JavaSciript가 웹 개발언어라는 이미지를 고정해 버렸습니다. JavaScript는 프로그래밍 언어계의 죠지 리브스(George Reeves), 슈퍼맨을 연기한 배우가 되었습니다. 하지만 JavaScript는 웹과 관련되지 않은 대규모의 응용 프로그램 개발에도 잘 어울리는 언어입니다.
움직이는 목표물

JavaScript는 첫 번째 버전은 매우 빈약했습니다. 예외 처리, 내부 함수, 상속 등의 기능이 없었습니다. 현재의 JavaScript는 완전한 객체지향 프로그래밍 언어입니다. 하지만 언어에 대한 여러 의견들은 여전히 예전의 성숙하지 못한 모습에 바탕을 두고 있습니다.

JavaScript 언어에 대한 관리 책임을 맡고 있는 ECMA 위원회는 비록 그 의도는 좋지만 이미 너무 많은 버전이 존재한다는 JavaScript의 가장 큰 문제점을 악화시키는 확장 작업을 하고 있습니다.
설계 오류

완벽한 프로그래밍 언어는 없습니다. JavaScript도 설계 오류를 포함하고 있습니다. + 를 자동 타입 변환과 함께 더하기와 문자열 병합이라는 두 가지 의미로 오버로딩한 것, 실수를 저지르기 쉬워서 사용을 피해야 하는 with 문 등이 여기에 해당합니다. 예약어 정책도 너무 까다롭습니다. 정규 표현식 기술 방법과 세미콜론 삽입은 아주 큰 실수입니다. 이런 실수는 프로그래밍 오류를 야기하고, 언어 설계 자체를 의문스럽게 만들게 됩니다. 다행히도, 이런 문제의 상당수는 lint라는 좋은 프로그램으로 완화가 가능합니다.

전체적인 언어 설계는 매우 안전합니다. 놀랍게도, ECMAScript 위원회는 이런 문제를 수정하는데 별로 관심이 없는 것처럼 보입니다. 아마도 그들은 새로운 것을 만드는데 더 관심이 많은 것 같습니다.
잘못된 구현들

JavaScript 초기 구현의 일부는 매우 버그가 많습니다. 이는 언어에 나쁜 영향을 미치고 있습니다. 설상가상으로 이런 구현들이 끔찍히도 버그가 많은 브라우저에 내장되어 있었습니다.
안 좋은 책들

사실상 JavaScript에 관한 거의 모든 책이 엉망입니다. 이 책들은 오류와 나쁜 예제투성이고, 잘못된 방법을 부추깁니다. 언어의 중요한 기능들이 잘못 설명되거나 아에 빠져있는 경우도 흔합니다. 필자는 십여 권의 JavaScript 책을 검토해보았는데, 다음 단 한 권의 책만 추천합니다. JavaScript: The Definitive Guide (5th Edition) by David Flanagan. (필자들에게: 좋은 책을 쓰셨다면 검토본을 보내주세요.)
불충분한 표준

언어의 공식 명세서는 ECMA가 작성하였습니다. 이 명세서는 품질이 매우 나쁩니다. 읽기도 어렵거니와 이해하기는 훨씬 어렵습니다. 이 제대된 책이 없는 문제와도 연결되는데, 저자들이 언어에 대한 그들의 이해를 높이기 위해 표준 문서를 활용할 수 없기 때문입니다. ECMA와 TC39 위원회는 깊이 반성해야 할 것입니다.
초보자들

JavaScript를 작성하는 대부분의 사람들은 개발자가 아닙니다. 그들은 좋은 프로그램을 쓰는 훈련을 받지 못했고 원칙이 부족합니다. JavaScript는 뛰어난 표현력을 가졌기에 그들은 어쨌건 유용한 프로그램을 작성할 수 있습니다. 이는 JavaScript는 단지 초보자들을 위한 것이지 전문가 용은 아니라는 인상을 주었습니다. 이는 사실이 아닙니다.
객체지향

JavaScript는 객체지향일까요? JavaScript는 데이터와 데이터를 처리할 수 있는 메서드를 담을 수 있는 오브젝트를 제공합니다. 오브젝트는 다른 오브젝트를 저장할 수도 있습니다. 클래스는 없지만, 클래스 변수와 메서드를 담을 수 있는 클래스 역할을 하는 생성자가 있습니다. 클래스 기반의 상속 기능은 없지만, 프로토타입 기반의 상속 기능을 제공합니다.

오브젝트 시스템을 만드는 두 가지 주요 방법은 상속(is-a)과 집합(has-a)이 있습니다. JavaScript는 두 가지 방법을 다 지원하지만, 언어의 동적인 속성은 집합(aggregation)일 때 더 빛을 발합니다.

일부에서는 JavaScript가 정보 은닉을 제공하지 않기 때문에 참된 의미의 객체지향이 아니라고 말합니다. 이는 오브젝트에 private 멤버와 메서드가 없다는 뜻입니다. 모든 멤버는 public입니다.

하지만 JavaScript 오브젝트는 private 변수와 private 메소드를 가질 수 있다.로 밝혀졌습니다. 물론, JavaScript는 세상에서 가장 오해가 많은 언어이기에 이를 이해하는 사람은 많지 않습니다.

또한 일부는 JavaScript가 상속을 지원하지 않기 때문에 진정한 의미의 객체 지향 언어라고 말합니다. 하지만 JavaScript는 전통적인 상속 방식뿐만 아니라 다른 코드 재활용 패턴 또한 제공한다. 라는 사실이 밝혀졌습니다.


[참조문서]

Copyright 2001 Douglas Crockford. All Rights Reserved Wrrrldwide.
JavaScript:
The World's Most Misunderstood Programming Language

Douglas Crockford
www.crockford.com

JavaScript, aka Mocha, aka LiveScript, aka JScript, aka ECMAScript, is one of the world's most popular programming languages. Virtually every personal computer in the world has at least one JavaScript interpreter installed on it and in active use. JavaScript's popularity is due entirely to its role as the scripting language of the WWW.

Despite its popularity, few know that JavaScript is a very nice dynamic object-oriented general-purpose programming language. How can this be a secret? Why is this language so misunderstood?
The Name

The Java- prefix suggests that JavaScript is somehow related to Java, that it is a subset or less capable version of Java. It seems that the name was intentionally selected to create confusion, and from confusion comes misunderstanding. JavaScript is not interpreted Java. Java is interpreted Java. JavaScript is a different language.

JavaScript has a syntactic similarity to Java, much as Java has to C. But it is no more a subset of Java than Java is a subset of C. It is better than Java in the applications that Java (fka Oak) was originally intended for.

JavaScript was not developed at Sun Microsystems, the home of Java. JavaScript was developed at Netscape. It was originally called LiveScript, but that name wasn't confusing enough.

The -Script suffix suggests that it is not a real programming language, that a scripting language is less than a programming language. But it is really a matter of specialization. Compared to C, JavaScript trades performance for expressive power and dynamism.
Lisp in C's Clothing

JavaScript's C-like syntax, including curly braces and the clunky for statement, makes it appear to be an ordinary procedural language. This is misleading because JavaScript has more in common with functional languages like Lisp or Scheme than with C or Java. It has arrays instead of lists and objects instead of property lists. Functions are first class. It has closures. You get lambdas without having to balance all those parens.
Typecasting

JavaScript was designed to run in Netscape Navigator. Its success there led to it becoming standard equipment in virtually all web browsers. This has resulted in typecasting. JavaScript is the George Reeves of programming languages. JavaScript is well suited to a large class of non-Web-related applications
Moving Target

The first versions of JavaScript were quite weak. They lacked exception handling, inner functions, and inheritance. In its present form, it is now a complete object-oriented programming language. But many opinions of the language are based on its immature forms.

The ECMA committee that has stewardship over the language is developing extensions which, while well intentioned, will aggravate one of the language's biggest problems: There are already too many versions. This creates confusion.
Design Errors

No programming language is perfect. JavaScript has its share of design errors, such as the overloading of + to mean both addition and concatenation with type coercion, and the error-prone with statement should be avoided. The reserved word policies are much too strict. Semicolon insertion was a huge mistake, as was the notation for literal regular expressions. These mistakes have led to programming errors, and called the design of the language as a whole into question. Fortunately, many of these problems can be mitigated with a good lint program.

The design of the language on the whole is quite sound. Surprisingly, the ECMAScript committee does not appear to be interested in correcting these problems. Perhaps they are more interested in making new ones.
Lousy Implementations

Some of the earlier implementations of JavaScript were quite buggy. This reflected badly on the language. Compounding that, those implementations were embedded in horribly buggy web browsers.
Bad Books

Nearly all of the books about JavaScript are quite awful. They contain errors, poor examples, and promote bad practices. Important features of the language are often explained poorly, or left out entirely. I have reviewed dozens of JavaScript books, and I can only recommend one: JavaScript: The Definitive Guide (5th Edition) by David Flanagan. (Attention authors: If you have written a good one, please send me a review copy.)
Substandard Standard

The official specification for the language is published by ECMA. The specification is of extremely poor quality. It is difficult to read and very difficult to understand. This has been a contributor to the Bad Book problem because authors have been unable to use the standard document to improve their own understanding of the language. ECMA and the TC39 committee should be deeply embarrassed.
Amateurs

Most of the people writing in JavaScript are not programmers. They lack the training and discipline to write good programs. JavaScript has so much expressive power that they are able to do useful things in it, anyway. This has given JavaScript a reputation of being strictly for the amateurs, that it is not suitable for professional programming. This is simply not the case.
Object-Oriented

Is JavaScript object-oriented? It has objects which can contain data and methods that act upon that data. Objects can contain other objects. It does not have classes, but it does have constructors which do what classes do, including acting as containers for class variables and methods. It does not have class-oriented inheritance, but it does have prototype-oriented inheritance.

The two main ways of building up object systems are by inheritance (is-a) and by aggregation (has-a). JavaScript does both, but its dynamic nature allows it to excel at aggregation.

Some argue that JavaScript is not truly object oriented because it does not provide information hiding. That is, objects cannot have private variables and private methods: All members are public.

But it turns out that JavaScript objects can have private variables and private methods. (Click here now to find out how.) Of course, few understand this because JavaScript is the world's most misunderstood programming language.

Some argue that JavaScript is not truly object oriented because it does not provide inheritance. But it turns out that JavaScript supports not only classical inheritance, but other code reuse patterns as well.

Copyright 2001 Douglas Crockford. All Rights Reserved Wrrrldwide.

'Programing > HTML/JavaScript/CSS' 카테고리의 다른 글

What is the Flapjax.  (0) 2007.04.19
Switching Tab  (1) 2007.04.18
Javascript 의 Object : new Object() ?===? {}  (0) 2007.04.06
AJAX 실전 #2 - 주소록 구현 준비  (1) 2007.03.30
Array.remove()  (3) 2007.03.28
posted by 초딩입맛제주아재
2007. 4. 6. 11:01 Programing/HTML/JavaScript/CSS
스쿨에 javascript 의 색다른 표현식에 관한 글이 올라왔다(by 행복한고니)

그중 new Object === {} 라는 고니님의 의견에 숨어지내리님이 급 제동을 걸어왔다.
너무나도 살벌한 분위기 속에서 결국 두 분의 논쟁에는 결론이 나질 않았고
나는 개인적으로 무소레즈님의 코멘트를 이 논쟁의 결론으로 치부한다.


무소레즈님의 댓글


---------------------------------------
1. new Object() === {} 가 같다?
2. Object 가 {} 으로 만들어진다?
3. constructor 의 비교로 객체가 똑같다고 말할수 있다?
4. Object 는 Function 의 instance 이다?
---------------------------------------

1. new Object() === {} 가 같다?
답변 1. 엄밀하게 다릅니다.

    ( new Object() === {} )  의 결과는 false 입니다.
    하지만 함수 객체타입은 같기에  instance 를 비교하면 true 입니다.
    왜냐하면 같은 Object 함수의 객체타입으로 생성되기 때문입니다.
    어떤 함수든 new 통하여 새로운 instance 개체를 생성하면
    어떤 instance 개체든 Object 함수 객체타입의 instance 를 갖습니다.

    예를 들어

        today = new Date();

        today 라는 instance 개체는
        Date 함수객체타입의 instance 이기도 하지만
        Object 함수 객체타입의 instance 이기도 합니다.

    또한 사용자 정의 함수를 통하여 새로운 instance 개체를 생성해도 마찬가지입니다.

        function funcName() {
        }
        fn = new funcName();

        fn 이라는 instance 개체는
        funcName 함수객체타입의 instance 이기도 하지만
        Object 함수 객체타입의 instance 이기도 합니다.

        fn 은 독립적인 instance 개체로서
        funcName 이라는 함수객체에서 정의된 구조(초기환경설정)의 instance 을 가졌다는 것이지요.

        만약 funcName 함수로 또다른 instance 개체를 생성한다면

        fn2 = new funcName();

        fn2 은 fn 과는 서로다른  instance 개체이지만
        funcName 이라는 함수명으로 정의된 구조를 가진 같은 instance 를 가집니다.
        fn1 과 fn2 는 같은 instance 를 가지고 있지만 서로 다른 개체입니다.


    (new Object) 와  {} 에의해 생성된 각각의 instance 객체변수로 비교하자면

        x1=new Object
        x2=new Object
        y1={};
        y2={};

        (x1 === x2);
        (y1 === y2)
        (x1 === y1);
        (x2 === y2);

    모두 false 입니다.  생성된 instance 개체가 서로 독립적이기 때문이지요.
    하지만  instanceof 연산자를 이용하여 비교해보면 모두 같은 instance 를 가지고 있습니다.

    Object 함수객체 타입은  ( Object.prototype.constructor ) 로 알아낼수 있습니다.
      결과는 :  function Object() { [native code] }
    funcName 함수객체 타입은  ( funcName.prototype.constructor ) 로 알아낼수 있습니다.
      결과는 : function funcName() { }

    (new Object ) 와 {} 는 미리 정의된 Object 함수 객체를 통하여
    서로다른 새로운 instance 개체들을 생성합니다.
    사용자 정의 함수에서 처럼  funcName 이 없기때문에
    Object 함수 객체타입의 instance 만을 갖게 되는 것입니다.

    ★ 그렇다고 (new Object) 와 {} 가 Object 함수객체 내부에서 주어진 정의가 완전히 같은가?
    그렇지 않습니다.
    {} 는 Object initializer (개체 초기화 지정자) 라고 불리우며,
    Object initializer 라 불리우는 {} 를 다른 함수의 내부에서 사용시
    Object initializer 를 호출할때 마다, {} 에의 정의된  속성과 그에 할당된 표현식이나 값을
    매번 다시 해석(interpret) 한다는 것이 (new Object) 와 다릅니다.

    함수내부에서 개체 초기화 지정자인 {} 를 정의하고
    그 함수를  호출하면 {} 에의해 정의된 속성과 그 값을 다시 해석하여
    매번 초기화 시킨다는것입니다.

        function funcName (cond) {
                A=3;
                B={ A:5 };
                C=new Object;
                Object.prototype.A=7;

                if(cond==1) return B.A;
                else if( cond==2) return C.A;
                else  return A;
        }

        A1=funcName(0);
        B1=funcName(1);
        C1=funcName(2);

        A2=funcName(0);
        B2=funcName(1);
        C2=funcName(2);


        A1 과 A2 는 메모리에 이미 저장된 "3" 이란 값을 반환 받습니다.
        C1 과 C2 도 메모리에 이미 저장된 "7" 이란 값을 반환 받습니다.
        B1 과 B2 는 각각 호출시 초기화 지정자를 재 해석하여 메모리에 다시 올려 놓고,
        그 후에 "5" 라는 값을 반환합니다.

        B 는  초기화 지정자 {}로 할당 받았기 때문에
        매번 함수를 호출 할때마다  B 의 속성과 값을 재 해석하여 메모리에 재 저장합니다.
        즉, 함수를 호출할때마다 매번 다시 초기화 시킨다는 말입니다.


        만약 함수 외부에서 초기화 지정자를 사용했다면
        var G={ A: 9 };
        G.A 를 호출할때 이미 초기화되어 메모리에 저장되어 있는 "9" 라는 값을 불러오게 됩니다.
        다른 곳에서 G.A 를 호출해도 역시 이미 저장되어 있는 값 "9"를 반환합니다.
        함수 내부에서 처럼 재 해석하여 재 초기화 시키는 것이 아닙니다.

        C1 과 C2 를 호출하면 이미 메모리에 저정되어 있는  "7" 이라는 값을 반환합니다.
        B1 과 B2 처럼 호출할때마다 재 초기화 시키는 것이 아닙니다.

        그렇게 (new Object ) 와 {} 는 다릅니다.


2. Object 가 {} 으로 만들어진다?
답변 2.
    {} 를 호출하면 내부에서 새로운 (new Object) 를 생성하여 instance 개체를 반환한다고 알고 있습니다.


3. constructor 의 비교로 객체가 똑같다고 말할수 있다?
답변 3.
    (객체.constructor) 의 사용은 생성자가 어떤 객체타입을 가지고 있는지 참조할수 있도록
    미리 정의되어 있는 Object 의 prototype 일뿐입니다.
    프로그램의 처리 로직에따라 객체의 속성으로써 prototype 정의는 언제든 변경 가능하며
    변경된 결과를 처리 조건에따라 필요할때 마다 활용할 수 있습니다.

    (개체.constructor) 는 
    개체를 새로 생성했을때 그 개체가
    어떤 객체 타입을 가진 생성자 함수로부터 생성된 개체인지를 참조할 수 있도록
    Javascript 에서 미리 정의해둔 하나의 속성 일뿐입니다.
    변경 불가한 상수가 아니란 것이지요.
    프로그램 처리의 상황에따라 그 값을 변경하며, 참조하여 활용할 수 있습니다.

    "constructor 의 비교로 객체가 똑같다고 말할수 있습니다."
    에대해서는 그럴수도 있고 아닐수도 있겠지요.
    프로그램의 처리 상황에따라 그 속성을 주어진대로 활용하거나, 변경하여 사용하거나
    아니면 사용하지 않거나..
    엿을 파는 엿장수 맘 아니겠습니까?

    엿장수 맘대로 사용했을때 문제가 발생할 여지가 있다면야 그렇게 사용해서는 안되겠지요.
    어느 분이든...  어떤 경우에 문제가 생기는지 알려주시면
    저도 다음부터는 유의하여 사용해 보겠습니다.


4. Object 는 Function 의 instance 이다?
답변 4.
    이미 정의되어 있는 Object 는 Function 함수 객체로부터 생성된 함수 객체이기때문에
        Function 함수 객체타입의 instance 를 갖습니다.
        또한 동시에 new 를 통하여 생성되기때문에 Object 객체타입의 instance  를 갖기도 합니다.


posted by 초딩입맛제주아재
2007. 3. 30. 14:33 Programing/HTML/JavaScript/CSS

이번 시간에는 주소록의 뼈대를 만들것이다.
간단한 DB 테이블을 하나 만들고, PHP를 이용해서 DB작업을 위한 서버프로그램을 만들것이다.
그리고 다음장에서 주소록의 핵심인 주소록의 데이터를 사용자가 입력/수정/삭제 할 수 있는 HTML페이지를 작성하도록 하겠다.

DB

주소록은 간단하게 만들기로 하였다.
이름,연락처,주소 그리고 인덱스.
총 4개의 필드만 필요하다. 뭐 더 만들고 싶은 필드가 있으면 그건 각자 알아서...

CREATE TABLE addressbook (
  idx INT NOT NULL AUTO_INCREMENT ,
  name VARCHAR( 30 ) NOT NULL ,
  tel VARCHAR( 30 ) NOT NULL ,
  addr VARCHAR( 100 ) ,
  PRIMARY KEY ( idx )
)



서버프로그램(PHP)

서버프로그램은 클래스를 사용하기로 한다.
혹, 클래스가 잘 적응이 안되는 분도 이기회에 클래스를 한번 써보는것도 나쁘지는 않으리라 생각한다.
클래스를 설계할때 고민해야 할 것은 역할의 분리이다. 하나의 메소드에게는 한가지 일만 시키는것이 좋다.

직원 A에게 복사를 하는 일과 서류를 작성하는일, 거래처를 관리하는 일을 모두 맡겨왔는데
갑작스레 A가 사표를 낸다면

  1. A가 해왔던 세 가지 일을 모두 할 줄 아는 직원을 뽑거나
  2. 각각의 일을 맡을 세명의 직원을 뽑아야 한다.

하지만 처음부터 각 업무를 서로 다른 사람이 해왔다면 필요한 일만 할줄 아는 사람을 새로 뽑으면 된다.

우리의 주소록은 비교적 간단하므로 클래스를 구성하는데 별 무리가 없을것이다.
주소록 클래스가 할일을 살펴보면

  1. 주소록에 새로운 데이터 추가
  2. 입력된 데이터의 수정/삭제
  3. 입력된 데이터 추출

이상이다.

그럼 클래스를 만들어보자.

* 주소록 클래스는 PHP4도 호환이 가능하도록 작성할 예정이므로 __constructor__ 같은 PHP5전용 코드는 사용하지 않을것이다.

[class_addressbook.php]

class AddressBook{
   var $conn;

   //생성자
   function AddressBook($conn=Null){
       if($conn == Null){
           return false;
       }

       $this->conn = $conn;
   }

   //데이터 입력
   function addAddress(){}

   //데이터 수정
   function updateAddress(){}

   //데이터 삭제
   function deleteAddress(){}

   //데이터 추출
   function getAddress(){}
}

AddressBook 클래스의 기본 골격은 위와 같다.
DB연동이 주된 업무이니 생성자에서는 DB 커넥터를 기본으로 지정한다.

이제 메소드를 하나하나 구현해보자

1.데이터 입력: addAddress()

입력해야 할 필드는 총 4개인데 그중 idx는 자동으로 생성되니 addAddress 메소드에는 3개의 값만 던져주면 되겠다. 바로 이름,연락처,주소이다. 그중 주소는 NOT NULL 이 아니니 이점에 유의하자

function addAddress($name,$tel,$addr=Null){
   if(empty($name) == true || empty($tel) == true){
       return false;
   }
   //주소값이 주어지지 않으면 db에 null 을 입력한다. 공백을 입력하게 되면 is null 같은 검색이 안된다.
   if($addr == Null){
       $addr = 'null';
   }else{
       $addr = "'".$addr."'";
   }

   $qry = "INSERT INTO addressbook SET\n"
           ."name='".$name."',"
           ."tel='".$tel."',"
           ."addr=".$addr;

   $res = mysql_query($qry,$this->conn);

   //입력이 성공하면 해당 row의 index 번호를 반환한다
   if($res != false){
       return mysql_insert_id();
   }else{
       return false;
   }
}


2.데이터 수정: updateAddress()

데이터의 수정은 데이터의 입력과 비슷하다. 단지 어떤 데이터를 수정하느냐를 결정하는 idx가 주어지는 것만 다를뿐이다.

function updateAddress($idx,$name,$tel,$addr=Null){
   if(empty($idx) == true || empty($name) == true || empty($tel) == true){
       return false;
   }

   if($addr == Null){
       $addr = 'null';
   }else{
       $addr = "'".$addr."'";
   }

   $qry = "UPDATE addressbook SET\n"
           ."name='".$name."',"
           ."tel='".$tel."',"
           ."addr=".$addr."\n"
           ."WHERE idx=".$idx;

   return mysql_query($qry,$this->conn);
}


3.데이터 삭제: deleteAddress()


데이터의 삭제는 더 간단하다. 삭제할 row의 idx값만 주면 된다.

function deleteAddress($idx=Null){
   if($idx == Null){
       return false;
   }

   $qry = "DELETE FROM addressbook WHERE idx=".$idx;

   return mysql_query($qry,$this->conn);
}



4.데이터 추출: getAddress() + getAllAddress()

데이터 추출은 좀 다르게 구현을 해야 한다. 하나의 레코드만 추출 할 경우와 전체 레코드를 추출할 경우를 구분해야 하기 때문이다. 위에서도 하나의 말했듯 메소드에게는 하나의 일만 시키는 것이 좋으므로 데이터 추출은 두개의 메소드로 구현하기로 하자 getAddress()와 getAllAddress() 가 바로 그것이다.

function getAddress($idx=Null){
   if($idx == Null){
       return false;
   }

   $qry = "SELECT * FROM addressbook WHERE idx=".$idx;
   $res = mysql_query($qry,$this->conn);

   if(mysql_num_rows($res) > 0){
       return mysql_result($res,0);
   }else{
       return Null;
   }
}
function getAllAddress(){
   $qry = "SELECT * FROM addressbook";
   $res = mysql_query($qry,$this->conn);

   if(mysql_num_rows($res) > 0){
       $result = array();
       while($row = mysql_fetch_assoc($res)){
           array_push($result,$row);
       }

       return $result;
   }else{
       return Null;
   }
}

이제 필요한 메소드가 다 만들어졌다.
그런데 데이터 입력과 수정작업을 처리하는 메소드에 중복되는 코드가 보인다.
바로 SQL 쿼리문을 작성하는 부분인데 이부분은 따로 쿼리 작성 메소드를 만들어서 빼는게 좋겠다.
이름하여 캡슐화...

function makeQuery($name,$tel,$addr=Null){
   if(empty($addr) == true){
       $addr = 'null';
   }else{
       $addr = "'".$addr."'";
   }

   $str = "name='".$name."',\n"
         ."tel='".$tel."',\n"
         ."addr=".$addr;

   return $str;
}

캡슐화를 했으니 addAddress()와 updateAddress()메소드를 수정해야한다.
전체를 수정하는건 아니고 SQL쿼리 작성 부분만 수정하면 된다.

addAddress()
   $qry = "INSERT INTO addressbook SET\n";
   $qry .= $this->makeQuery($name,$tel,$addr);

updateAddress()
   $qry = "UPDATE addressbook SET\n";
   $qry .= $this->makeQuery($name,$tel,$addr);
   $qry .= " WHERE idx=".$idx;

드디어 AddressBook 클래스가 완성됐다.
사실 제대로된 객체지향 프로그래밍을 하자면 AddressBook 클래스 외에 Address 클래스도 필요하다.
하지만 지금까지의 과정도 쉽고 단순하게 하자는 당초의 계획과는 달리 복잡해진감이 없지않으므로 Address클래스는 일단 생략하고 클라이언트 프로그램 완성 후에 리팩토링을 하겠다.


테스트

DB에 테이블도 만들었고 클래스도 만들었으니 테스트를 해보자.
웹프로그래밍에 TDD(Test Driven Develope)적용이 필요한지에 대해서는 의견이 분분하지만 개념을 알필요는 충분하다고 본다.

에디터에서 다음과 같은 코드를 작성한 후 브라우져로 결과를 확인해보자.
<?php
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_QUIET_EVAL, 1);
assert_options(ASSERT_CALLBACK, 'assert_handler');
assert_options(ASSERT_BAIL,1);

function assert_handler($file,$line,$code){
   echo "Assertion Failed:<br />
       File '$file'<br />
       Line '$line'<br />
       Code '$code'<br />";
}

include_once 'db_conn.php'
include_once 'class_addressbook.php';

echo 'start test<br />';

assert('$BOOK = new AddressBook($conn)');
echo 'class construction test ... [OK]<br />';

assert('$BOOK->addAddress("이효리","011-1111-1111")');
assert('$BOOK->addAddress("강동원","011-2222-2222","우리집")');
assert('$BOOK->addAddress("Jackson","060-3333-3333","America")');
echo 'address data INSERT test ... [OK]<br />';

assert('$BOOK->updateAddress(1,"이효리","016-4444-4444","우리집")');
assert('$BOOK->updateAddress(2,"강동웜","011-2222-2222","북한")');
echo 'address data UPDATE test ... [OK]<br />';

assert('$BOOK->getAddress(3)');
echo 'an address data GET test ... [OK]<br />';

assert('$BOOK->deleteAddress(3)');
echo 'an address data DELETE test ... [OK]<br />';

assert('$BOOK->getAllAddress()');
echo 'all address data GET test ... [OK]<br />';

echo 'All Test Success!!';

$qry = "DELETE FROM addressbook";
mysql_query($qry,$conn);

$qry = "ALTER TABLE addressbook auto_increment=1";
mysql_query($qry,$conn);
?>

브라우져에 All Test Success!! 가 출력됐다면 성공이다.

테스트가 성공했으면 실제 쓸 소스를 작성해야 한다.
이미 테스트 코드에서 다 만들었으니 그냥 가져다 쓰면 된다.

[execute_address.php]

//include =================================================
include_once 'db_conn.php';
include_once 'class_addressbook.php';


//request vars ============================================
$name = isset($_POST['name'])?$_POST['name']:Null;
$tel     = isset($_POST['tel'])?$_POST['tel']:Null;
$addr  = isset($_POST['addr'])?$_POST['addr']:Null;
$idx    = isset($_POST['idx'])?$_POST['idx']:Null;
$job    = isset($_POST['job'])?$_POST['job']:Null;

$result = false;

if($job == Null || false !== $BOOK = new AddressBook($conn)){
   switch($job){
       case('add'):
           $result = $BOOK->addAddress($name,$tel,$addr);
       break;

       case('update'):
           $result = $BOOK->updateAddress($idx,$name,$tel,$addr);
       break;

       case('delete'):
           $result = $BOOK->deleteAddress($idx);
       break;

       case('get'):
           $result = $BOOK->getAddress($idx);
       break;

       case('get_all'):
           $result = $BOOK->getAllAddress();
       break;
   }
}


준비끝

아직 완성은 아니지만 서버쪽 코딩이 끝났다.
완성이 아니라고한 이유는 php 처리 결과를 javascript 로 보내는 코드가 빠졌기 때문이다.
이 부분은 클라이언트 코딩할때 추가하기로 한다. 한글 인코딩 문제도 있고 JSON과 XML등등 데이터 가공과정을 거쳐야 하기때문이다.

여기까지는 그리 힘들지 않을것이라 본다.
어려운 부분은 없었던것 같고, 다만 클래스나 assert() 함수가 낯선 분들이 있을지도 모르겠다.
다음 포스팅에서는 AJAX애플리케이션의 핵심인 클라이언트 코딩을 하겠다.
DOM을 많이 사용해야하니 DOM에 자신 없는 분은 미리 [DOM]에 대해 공부를 하시길...
다음 링크를 따라가면 DOM에 대해 배울수 있다.

DOM3 - http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/Overview.html#contents
DOM3 Event - http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html
HTML DOM - http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-39872903

posted by 초딩입맛제주아재
2007. 3. 28. 13:22 Programing/HTML/JavaScript/CSS

개요

이번에 AJAX 실전 테스트용 주소록 코드를 만들면서 배열의 remove 기능이 필요해서 Javascript API를 보니 Array 객체에 remove 메소드가 없다.(혹시 필자가 못찾은 거라면 낭패 -_-.... 찾은분은 알려주시길..)
코어에서 지원이 안되니 직접 만들수 밖에 없었다.
본인이 손수만든 Array.remove() 메소드를 공개한다.
누누히 당부드리지만 본인은 신이 아니고 프로그래밍 도사도 아니기에 실수도 할 수 있고 효율이 떨어지는 코드를 남발할수도 있다. 조언과 지적은 감사히 수용하겠으나 막가파식 테클은 반사다...:)


발단

주소록의 정보를 배열에 담았다가 삭제할때 인덱스를 테이블 ROW(tr)과 일치시켜 주어야 하기때문에
인덱스의 재정렬이 필요했다.

* tableSection.deleteRow() 메서드는 자동으로 ROW의 인덱스가 재정렬한다.
* http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-67417573 참고.


사용자 삽입 이미지

주소록 리스트 - 여기서 데이터를 삭제하면...

사용자 삽입 이미지
사용자 삽입 이미지

테이블 ROW 가 삭제되고 주소록 정보를 담았던 배열에서도 해당 데이터를 담고 있는 원소가 삭제된 후 인덱스를


전개

배열의 원소를 삭제하고 인덱스를 정렬하는 알고리듬을 생각해보면 다음과 같은 절차를 거친다.

  1. 삭제할 원소의 인덱스 직전까지의 원소들과 이후의 원소들을 분리시킨다.
  2. 분리된 원소들을 하나의 배열로 합친다.
원리는 무척 간단해 보인다.
방법이 문제다.
어떻게 배열을 둘로 나눌지...

본인은 API를 보던중 slice 라는 메소드를 찾았다.

출처 : http://www.w3schools.com/jsref/jsref_slice_array.asp

Definition and Usage

The slice() method returns selected elements from an existing array.

Syntax

arrayObject.slice(start,end)

Parameter Description
start Required. Specify where to start the selection. Must be a number
end Optional. Specify where to end the selection. Must be a number


Tips and Notes

Tip: You can use negative numbers to select from the end of the array.

Note: If end is not specified, slice() selects all elements from the specified start position and to the end of the array.


이 메소드를 사용하면 간단하게 해결될것 같지 않은가?

그리하여 다음과 같은 코드를 만들었다.

   //addressbook 는 주소록 정보를 가지고 있는 배열이다.

   var temp_head = addressbook.slice(0,rowIndex);
   var temp_body = addressbook.slice(rowIndex + 1);

   for(var i=0; i<temp_body.length; i++){
       temp_head.push(temp_body[i]);
   }

   addressbook = temp_head;

주의해야 할점은 slice메소드의 첫번째 인자와 두번째 인자의 시작 위치가 다르다는 것이다.
start값은 0부터 시작하는 배열의 첨자값을, end값은 1부터 시작하는 배열의 length 값을 의미한다.



절정

위 코드를 만들고 나니 아무래도 remove 기능을 모든 배열에서 쓰게 하는게 낫겠다는 생각이 들었다.
Array객체에 prototype으로 remove 메서드를 추가하기로 했다.

Array.prototype.remove = function(index){}

처음엔 위의 코드를 그대로 복사해서 가져다 붙였다. Ctrl + c, Ctrl + v 신공...

[code 1]

  var temp_head = this.slice(0,index);
   var temp_body = this.slice(index + 1);

   for(var i=0; i<temp_body.length; i++){
       temp_head.push(temp_body[i]);
   }

   this = temp_head;


어째 좀 이상하지 않은가??
자신이 소유하고 있는 메소드에서 자신을 바꾸려하다니....
익스플로러에서는 this에 할당 할 수 없다는 오류가 발생한다.
시도 자체가 말이 안되는 행위였다...-_-;;

그래서 다음과 같은 알고리듬을 생각했다.
  1. 위,아래 원소를 분리하고 합치는것까지는 동일
  2. 원본배열의 원소를 전부 제거
  3. 새로 만들어진 배열의 원소를 원본배열에 모두 추가
[code 2]

   var temp_head = this.slice(0,idx);
   var temp_body = this.slice(idx + 1);

   for(var i=0; i<temp_body.length; i++){
       temp_head.push(temp_body[i]);
   }

   while(this.length > 0){
       this.pop();    //shift 를 사용해도 된다.
   }

   while(temp_head.length > 0){
       this.push(temp_head.shift());
   }

이제 제대로 작동한다~

그런데 웬지 모르게 메소드가 지저분해 보인다.
for에 while이 두개씩이나...반복문이 세번이나 돌아간다...

그리하여 또 다시 머리를 쥐어짰다.
  1. 원본 배열에서 주어진 인덱스부터 마지막 원소까지 모두 추출한다.
  2. 추출된 배열에서 마지막 원소는 제거할 원소이므로 빼버린다.
  3. 남은 원소들을 원본 배열에 추가한다.
  
[code 3]  

   var temp = new Array();
   var i = this.length;

   while(i > idx){
       var kk = this.pop();
       temp.push(kk);

       i--;
   }

   temp.pop();

   while(temp.length > 0){
       this.push(temp.pop());
   }


결말

[code 2]와 [code 3] 의 효율성의 차이는 정확히 파악하지 못했다.
단지 보기에 깔끔하고 반복문을 한번 덜 쓴다는 이유로 [code 3]을 주소록에 적용을 했다.

[code 3]의 경우 아직 리팩토링의 소지가 있는데 바로 두번째 while 블럭 이다.
조건절에서는 함수나 메소드의 호출을 지양해야 하기 때문인데

다음과 같이 수정 할 수 있다.

   var temp = new Array();
   var i = this.length;

   while(i > idx){
       var kk = this.pop();
       temp.push(kk);

       i--;
   }

   for(var i=temp.length - 2; i>=0; i--){
       this.push(temp[i]);
   }

중간에 temp.pop()이 필요가 없어졌고 for문을 사용한다.
조건절에서는 i가 0이상인지만 비교하면 된다.

사용예)

<script type="text/javascript">
Array.prototype.remove = function(idx){
   var temp = new Array();
   var i = this.length;

   while(i > idx){
       var kk = this.pop();
       temp.push(kk);

       i--;
   }

   for(var i=temp.length - 2; i>=0; i--){
       this.push(temp[i]);
   }
}

var arr = new Array(6)
arr[0] = "Jani"
arr[1] = "Hege"
arr[2] = "Stale"
arr[3] = "Kai Jim"
arr[4] = "Borge"
arr[5] = "Tove"

document.write(arr + "<br />")
arr.remove(1);
document.write(arr + "<br />")
</script>

posted by 초딩입맛제주아재
prev 1 2 3 4 next