1. 2007.08.24 UI 대체는 내게 맡겨라 Ajax를 이용한 UI 개발

UI 대체는 내게 맡겨라 Ajax를 이용한 UI 개발

UI 대체는 내게 맡겨라 Ajax를 이용한 UI 개발


액티브X를 사용하는 가장 큰 이유 두 가지는 클라이언트 자원을 이용할 수 있다는 점과 윈도우의 UI 컴포넌트를 쉽게 사용할 수 있다는 점이다. Ajax는 이 두 가지 용도를 모두 만족시킬 수는 없지만 고급 UI를 필요로 하는 애플리케이션까지 Ajax로 만들 수 있을 정도로 강력한 UI 기능을 지원한다. 특집 3부에서는 이점에 포커싱하여 Ajax로 액티브X를 대체하는 방법들에 대해 알아본다.


박영록 | poci@ncsoft.net | 오픈마루 개발자


먼저 액티브X가 할 수 있는 일에 대해서 알아보자. 크게 두 가지로 분류할 수 있을 것 같다. 하나는 클라이언트의 자원을 이용하는 일이다. 웹 브라우저의 보안 모델에서는 클라이언트의 자원 중 일부분만 접근할 수 있는데 액티브X를 사용하면 클라이언트의 거의 모든 자원을 이용할 수 있다. 인터넷 뱅킹을 할 때 뜨는 키보드 보안 모듈은 하드웨어 제어를 통해 키보드의 입력을 다른 프로그램이 해킹하지 않는지 감시하는 것이다. 바이러스 검사 프로그램 같은 건 클라이언트의 파일 시스템에 접근할 수 있기 때문에 가능한 것이고 자신의 PC를 다른 사설 네트워크(주로 회사 네트워크)에 소속된 것처럼 만들어주는 VPN(가상 사설 네트워크) 프로그램도 클라이언트의 네트워크 자원을 이용할 수 있기 때문에 가능한 것이다. 물론 게임도 가능하다. 이처럼 클라이언트의 자원을 마음대로 활용할 수 있기 때문에 강력한 도구가 되기도 하지만 스파이웨어 같은 도구로 악용되기도 한다.

또 다른 하나는 윈도우의 UI 컴포넌트들을 쉽게 활용할 수 있다는 것이다. 기본적인 컴포넌트 외에는 모두 직접 만들어야 하는 HTML과는 달리 윈도우의 UI 컴포넌트는 종류도 많고 완성도가 높은데다 GUI 생성 툴도 좋기 때문에 기능성이 높은 UI를 쉽게 만들 수 있다. 그래서 고객이 제한된 SI(시스템 통합) 프로젝트에서는 복잡한 요구사항을 처리하기 위해 액티브X를 선택하는 경우가 많다. 간혹 더 화려하고 막강한 UI를 위해서 액티브X를 쓴다고 하기도 하는데 실상 HTML과 CSS, 자바스크립트로도 고수준 UI를 만드는 일이 그리 어려운 일은 아니다. 그보다 이미 준비되고 검증된 UI 컴포넌트가 많고 겉보기 UI만 만들 수 있는 HTML 위지윅 도구에 비해 동작까지 세밀하게 조정할 수 있는 비주얼 스튜디오가 더 편리하다는 점이 중요하게 작용하는 것 같다.

그럼 Ajax가 할 수 있는 일은 무엇일까. Ajax는 원래는 웹에서 비동기 통신을 하는 기술을 일컫는 말이었다. 하지만 요즘은 의미가 확장되어 DHTML, CSS, 자바스크립트를 포괄하는 개념으로 이야기되기도 한다. 여기서도 이런 넓은 의미의 개념을 Ajax에 적용하는 것이 적절할 듯하다. 이런 측면에서 본다면 Ajax가 대체할 수 있는 것은 앞의 두 가지 중 후자다. Ajax 역시 웹 브라우저 보안 모델을 넘어서진 못하기 때문에 전자의 문제는 전혀 해결할 수 없다. 그런 면에서 Ajax는 액티브X의 완전한 대안은 아닌 셈이다. 하지만 후자의 경우는 상당히 많은 부분을 대체할 수 있다. 요즘에는 워드프로세서나 스프레드시트처럼 고급 UI를 필요로 하는 애플리케이션도 액티브X를 전혀 쓰지 않고 만들어내는 경우도 볼 수 있을 정도다.


<화면 1> 위키 기반 웹노트 서비스 : 스프링노트

특집 3부에서는 Ajax로 액티브X를 대체하는 몇 가지 사례들에 대해 알아볼 것이다. 가급적 추상적인 이야기들보다는 코드로 이야기하면서 자바스크립트와 HTML/CSS로 UI를 만들어 나가는 것이 생각보다 쉽고 재미있다는 사실을 보여주고 싶다.



HTML WYSIWYG 에디터 만들기



과거 단순 텍스트만 저장하던 게시판이 점점 진화하여 다양한 스타일을 표현하려는 욕구가 강해지면서 카페나 블로그 등에도 액티브X나 자바 애플릿으로 만든 에디터가 많이 채용되었다. 그런데 요즘에는 액티브X 에디터들이 순수 HTML/자바스크립트 에디터가 점차 늘어나는 추세다. 최신 웹 브라우저들에 위지윅(WYSIWYG) 에디터가 탑재되면서 액티브X 없이 구현할 수 있게 된 것이다. 웹 브라우저에 내장된 위지윅 에디터를 사용하면 게시판 등에 위지윅 에디터를 붙이는 일은 별로 어렵지 않다. 이미 오픈소스로 완성도 높은 에디터도 많이 나와 있지만 여기서는 직접 만들어보자. 파이어폭스와 IE의 API가 조금씩 다르기 때문에 그 차이점도 조금 살펴볼 것이다.


HTML 편집 모드


위지윅 에디터를 이용하는 방법은 두 가지다. 하나는 자바스크립트에서 document.designMode에 ‘on’ 값을 주는 것이다. 그러면 페이지 전체가 에디팅 할 수 있는 모드로 바뀌게 된다. 이 방법은 파이어폭스와 IE 모두 동작한다. <리스트 1>을 보자.

<리스트 1> Hello World

<html>
<head>
<script type="text/javascript">
document.designMode = 'on'
</script>
</head>
<body>
<h2>Hello</h2>
<p>World</p>
</body>
</html>

<리스트 1>을 브라우저로 열면 <화면 2>와 같이 보인다.


<화면 2> <리스트1>의 실행 결과

겉보기론 별 다를 바 없어 보이지만 이 페이지는 브라우저상에서 편집이 가능하다. 만약 e와 l 사이에 커서가 있는 상태에서 a를 입력하면 Heallo가 보일 것이다. 물론 스타일은 주변 스타일을 그대로 유지한다. 화살표 키로 이동하는 것도 일반 에디터와 비슷하고 글자를 입력하거나 지우는 것도 비슷하다. 웹 브라우저 자체에서 키 입력을 받아서 HTML 문서의 DOM 객체를 수정하는 것이다. 그래서 DOM Inspector 등으로 보면 <h2> 엘리먼트의 내용이 Heallo로 바뀌는 것을 볼 수 있다.

IE에는 designMode와는 별개로 또 contentEditable이라는 속성이 있다. designMode가 HTML 문서 전체를 편집할 수 있도록 하는데 반해 contentEditable 속성은 document 객체의 속성이 아니라 엘리먼트의 속성으로 HTML 문서의 일부분만 편집할 수 있도록 할 수 있다. div나 span, a 등의 엘리먼트에 적용할 수 있다. 그래서 웹 에디터를 만들기는 파이어폭스보다 IE가 조금 더 쉽다. 반면 designMode는 문서 전체를 편집할 수 있도록 만들기 때문에 메뉴나 내비게이션까지 지워버릴 수도 있고 또 designMode에서는 링크를 클릭해도 이동하지 않기 때문에 지우지 않더라도 동작하지 않는다. 그래서 designMode만 지원하는 브라우저에서는 위지윅 에디터를 만들 때 iframe을 사용한다. iframe 안에 designMode를 켠 문서를 넣고 바깥에 메뉴나 내비게이션이 있는 문서는 designMode를 끈 상태로 놔두는 것이다. 그러면 iframe 영역만 자유롭게 편집할 수 있다. <리스트 2>와 비슷한 식으로 만들면 된다.

<리스트 2> 에디터 기본 틀

<body>
<div>
<a href="...">메뉴1</a>
<a href="...">메뉴2</a>
<a href="...">메뉴3</a>
</div>
<iframe id="editor" onload="this.contentWindow.document. designMode='on';"></iframe>
</body>

<리스트 2>를 실행하면 <화면 3>과 같이 표시된다. 이때, 박스 바깥은 일반 웹페이지처럼 동작하고 박스 안의 영역은 편집 가능한 상태가 된다. 윈도우 프로그래밍으로 비유한다면 RichEdit 컨트롤을 사용할 수 있게 된 셈이다. 하지만 이것만으로는 완전한 에디터가 될 수 없다. 위지윅 상태의 문서를 그 스타일 그대로 글을 쓰거나 지우거나 할 수는 있지만 새로운 스타일로 글을 쓰지는 못하기 때문이다. 예를 들어 World에서 첫 번째 예제 코드의 <p>World</p> 부분을 편집할 때 or 부분을 지우거나 다른 글자로 바꿀 수는 있어도 이 부분만 강조하기 위해 <p>W<strong> or</strong>ld</p>와 같이 만들 수는 없다. 쉽게 말하면 새로운 태그를 삽입할 수단이 없다. 엔터키를 누르면 <p> 태그나 <br>태그가 삽입되는 게 전부이다.


<화면 3> <리스트 2>의 결과화면


<화면 4> tinyMCE

그래서 에디터를 완성하기 위해서는 여러 가지 버튼들을 붙이고 단축키를 할당해서 새로운 태그를 삽입할 수 있게 해야 한다. 다음은 오픈소스 에디터인 tinyMCE의 화면 중 하나인데 이처럼 만들어야 완성된 에디터가 되는 것이다.


Bold 버튼 만들기


여기서는 간단하게 선택된 부분을 Bold로 강조하는 버튼을 만들어서 붙여보자. 먼저, 윈도우 프로그래밍에서 RichEdit 컨트롤을 쓰려면 갖다 붙이는 것뿐만 아니라 RichEdit 컨트롤의 API를 알아야 하듯이 웹 에디터에서도 웹 브라우저에서 제공하는 에디터의 API를 알아야 한다. 이것도 파이어폭스와 IE가 약간 다르긴 하지만 상당히 비슷한 편이다. 둘 다 document 객체에서 execCommand라는 메소드를 사용할 수 있다. execCommand의 사용법은 다음과 같다.

bSuccess = object.execCommand(sCommand [, bUserInterface] [, vValue])

IE에서는 selection 객체나 range 객체에도 이 메소드를 사용할 수 있다. 첫 번째 sCommand는 실행할 명령의 종류, 두 번째는 UI가 붙느냐 아니냐인데 일단은 무조건 false를 준다고 생각하면 된다. 세 번째는 명령을 실행하는데 부가적으로 필요한 값들을 넘겨주는 것이다. 대부분의 편집 API는 이 execCommand 메소드 하나에서 sCommand를 다양하게 바꿔가면서 실행하게 된다. sCommand의 종류로는 bold, createlink, fontname, fore color, formatblock 등 에디터에 필요한 다양한 것들이 있으며 자세한 내용은 파이어폭스 사이트나 MSDN에서 찾아볼 수 있다.

여기서는 강조하는 버튼을 붙일 것이므로 필요한 커맨드는 bold다. 바로 앞의 예제 코드에서 div 태그 안에 <리스트 3>과 같은 코드를 삽입해보자.

<리스트 3> 강조 버튼

<script type="text/javascript">
function editorDocument() {
return document.getElementById('editor').contentWindow.document
}
</script>
<a href="#" onmousedown="return false;" onclick= "editorDocument().execCommand('bold', false, true); return false;">강조</a>

그리고 웹 브라우저에서 다시 html 파일을 열어보자. 링크가 하나 더 생겼다. 이제 편집을 하다가 강조 링크를 누르면 그 다음부터 입력하는 글자는 진하게 표시될 것이다. 일부분을 선택하고 강조 링크를 누르면 그 부분만 강조된다. 이런 식으로 에디터에 필요한 다양한 기능들을 추가하면 앞서 본 tinyMCE와 같은 웹 에디터를 만들 수 있다.


<화면 5> <리스트 3>의 결과화면

이처럼 생각보다 웹 에디터를 만드는 일이 어렵지 않음을 알 수 있다. 하지만 그렇다고 또 쉬기만 한 일도 아니다. 브라우저마다 동작이 조금씩 다르고 웹 브라우저에 내장된 에디터에 버그가 많기 때문이다. 그래서 tinyMCE 같은 에디터에서는 이런 부분을 많이 수정해 놓았고 필자가 개발에 참여했던 스프링노트에서도 처음에는 tinyMCE를 쓰다가 수많은 버그를 견디지 못하고 에디터를 처음부터 다시 만들기도 했다. 아무튼 이제 사용자들에게 액티브X를 설치하도록 강요하지 않고도 꽤 괜찮은 수준의 위지윅 에디터를 제공할 수 있다는 것은 희망적인 일이다.


드래그 앤 드롭 기능 만들기



웹 페이지 내에서의 드래그 앤 드롭도 자바스크립트와 CSS만으로 구현할 수 있다. 드래그 앤 드롭은 윈도우처럼 웹 브라우저 내의 대화상자를 끌어서 움직인다든지, 트리에서 항목을 끌어서 옮기는 등 다양한 응용이 가능하기 때문에 직관적인 UI를 만드는데 유용하다. 만드는 방법도 어렵지 않다. 우선 CSS에는 절대 좌표로 특정 엘리먼트의 위치를 지정할 수 있는 기능이 있다. 자바스크립트에서는 다음과 같은 코드로 CSS style을 지정할 수 있다.

element.style.top = ‘200px’
element.style.left = ‘150px’

이것은 element를 위에서 200 픽셀, 왼쪽에서 150 픽셀의 위치에 놓으라는 것이다. 이 기능을 이용하면 특정 엘리먼트의 위치를 마음대로 조정할 수 있다. 또, 자바스크립트에서는 마우스 이벤트를 잡아내서 그 지점의 좌표를 알아낼 수 있다. 마우스를 누를 때 mousedown, 움직일 때 mousemove, 뗄 때 mouseup 이벤트가 발생하는데 이것을 이용하면 된다. 좌표는 프로토타입의 이벤트 객체를 통해서 쉽게 가져올 수 있다. 그리고 엘리먼트의 현재 위치도 자바스크립트에서 element.offsetLeft, elem ent.offsetTop 속성으로 알 수 있다. 엘리먼트의 좌표를 읽을 때는 자바스크립트로, 수정할 때는 CSS의 style 속성으로 쓴다는 점에 주의해야 한다. 이 정도만 알면 나머지는 산수만 하면 된다.


대화상자 만들기


먼저 문서에 대화상자 하나를 그려보자. 이번 예제부터는 프로토타입 라이브러리를 사용할 것이다.

<리스트 4> 대화상자 만들기

<html>
<head>
<script type="text/javascript" src="prototype.js"></script>
<style>
#dialog {

left: 150px;
top: 100px;
padding: 1em;
background-color: lightgreen;
}
</style>
</head>
<body>
<div id="dialog">
Hello
</div>
</body>
</html>

<리스트 4>를 실행시키면 <화면 6>과 같은 화면이 표시될 것이다. 팝업 윈도우가 아니라 HTML 문서 안에 div 태그를 띄워서 마치 대화상자처럼 보이게 만든다.


<화면 6> <리스트 4>의 결과화면


이벤트 핸들러 설정하기


우리는 여기서 Hello가 있는 대화상자를 마우스로 누르고 움직이면 대화상자가 따라 움직이도록 만들 것이다. 먼저 HTML 문서에 이벤트를 걸어야 한다. Hello가 있는 div 태그에서 마우스를 누르면 드래그가 시작되므로 div 태그에 onmousedown 이벤트 핸들러를 설정한다. 그리고 마우스가 이동하는 범위는 문서 전체이므로 문서 전체에 onmousemove 이벤트 핸들러를 걸고 드래그 종료를 확인하기 위해 onmouseup 이벤트도 문서 전체에 걸어준다. 위의 코드를 다음과 같이 수정하면 된다.

<body onmousemove=“drag($(‘dialog’), event)” onmouseup=“dragEnd($(‘dialog’), event)”>
<div id=“dialog” onmousedown=“dragBegin(this, event)”>

이벤트 핸들러에서 함수를 실행하면서 두 개의 인자를 넘긴다. 첫 번째 인자는 div 태그의 DOM 객체이고 두 번째 인자는 이벤트의 속성을 담고 있는 객체이다. 이벤트 핸들러에서는 이 두 가지 인자를 가지고 조작하게 된다.


드래그 이벤트 핸들러 만들기


그럼 이제 각 이벤트에 따라 동작하는 실제 이벤트 핸들러 함수를 만들어보자. 먼저 dragBegin에서 해야 할 일은 드래그를 시작한 지점의 좌표를 기억하고 드래그 시작을 알려주는 것이다. 다음과 같은 코드면 충분하다.

<리스트 5> 드래그 시작 알리기

function dragBegin(element, event) {
element.dragging = true
element.startx = Event.pointerX(event)
element.starty = Event.pointerY(event)
}

element 객체에 바로 우리가 정의한 속성을 대입하는 것이 특이해보일 수도 있을 것이다. DOM 객체도 일반 자바스크립트 객체와 똑같이 다룰 수 있고 자바스크립트는 속성을 동적으로 할당할 수 있기 때문에 <리스트 5>와 같은 코드가 가능하다. 보통 DOM 객체에 대응하는 자바스크립트 객체를 proxy 객체로 만들어서 쓰는 경우가 많은데 DOM 객체 자체에 메소드와 속성을 부여해서 실제로 드래그할 수 있는 객체인 것처럼 다룰 수도 있다. 지금은 드래그 앤 드롭의 원리를 보여주는 것이 목적이기 때문에 간편한 이 방법을 선택했지만 상황에 따라 다른 선택이 가능할 것이다.

이제 처음 드래그한 지점은 저장했으니 drag 함수에서는 마우스가 이동한 거리를 계산해서 그만큼 div 태그를 이동시키면 된다. <리스트 6>과 같은 코드가 될 것이다.

<리스트 6> 드래그 함수 적용

function drag(element, event) {
if (!dragging) return
element.style.left = element.offsetLeft + Event.pointerX(event) - element.startx + 'px'
element.style.top = element.offsetTop + Event.pointerY(event) - element.starty + 'px'
element.startx = Event.pointerX(event)
element.starty = Event.pointerY(event)
}

여기까지 하고 실행해보면 이제 Hello가 마우스를 따라다니는 것을 확인할 수 있을 것이다. 이제 마우스 버튼에서 손을 뗐을 때 드래그 안 되도록 하는 코드만 넣으면 된다.

function dragEnd(element, event) {
element.dragging = false
}

이걸로 드래그 앤 드롭의 완성이다. 불과 함수 세 개를 사용한 열 몇 줄의 코드로 드래그 앤 드롭이 구현되는 것이다.


드래그 앤 드롭 코드 줄이기


산수만 잘한다면 좀 더 줄일 수도 있다. 사실 마우스를 클릭한 지점과 대화상자의 좌상단과의 거리는 늘 일정하게 유지된다. 그렇다면 이 차이만 처음에 구해두면 중간에 변하는 값을 매번 저장하지 않아도 된다. 즉, 다음처럼 코드를 줄일 수 있다.

<리스트 7> 개선된 드래그 앤 드롭 코드

function dragBegin(element, event) {
element.dragging = true
element.diffx = Event.pointerX(event) - $('dialog').offsetLeft
element.diffy = Event.pointerY(event) - $('dialog').offsetTop
}
function drag(element, event) {
if (!element.dragging) return
element.style.left = Event.pointerX(event) - element.diffx + 'px'
element.style.top = Event.pointerY(event) - element.diffy + 'px'
}
function dragEnd(element, event) {
element.dragging = false
}

드래그가 어려운 기술일 것 같지만 의외로 이렇게 간단하게 구현된다. 드롭은 dragEnd에서 다른 함수를 호출할 수 있도록 하는 코드만 조금 추가하면 된다. 이것은 독자의 몫으로 남겨둔다.

또 하나 신경 써야 할 것은 이벤트 핸들러다. 여기서는 간단하게 구현하기 위해 엘리먼트에 직접 이벤트 핸들러를 달았지만 실전에서는 이벤트 핸들러도 동적으로 할당한다. body 엘리먼트처럼 넓은 범위에 mousemove처럼 자주 발생하는 이벤트를 걸어두는 것은 바람직하지 않기 때문이다. 그래서 body에 이벤트를 거는 것은 대화상자에 mousedown 이벤트가 발생한 시점에 걸고 mouseup 이벤트가 발생했을 때 이벤트 핸들러를 제거하는 것이 좋다. dragBegin, dragEnd 함수를 조금만 수정하면 쉽게 할 수 있을 것이다. script.aculo.us 같은 프레임워크에서 드래그 앤 드롭을 구현하는 방법도 크게 다르지 않다. 다만 좀 더 기능이 많기 때문에 코드는 훨씬 긴데 여기 있는 정도의 원리만 이해하면 script.aculo.us의 드래그 앤 드롭 코드도 쉽게 이해하고 활용할 수 있을 것이다.

한 가지 짚고 넘어가야 할 것은 모든 종류의 드래그 앤 드롭이 가능한 것은 아니라는 사실이다. 웹 브라우저의 내장 에디터가 어느 정도 기본 드래그 앤 드롭을 지원하기도 하고 HTML 문서 안에서도 위와 같은 방식으로 드래그 앤 드롭이 가능하지만 클라이언트에 있는 파일의 드래그 앤 드롭과 연계하는 것은 불가능하다. 탐색기에서 이미지를 끌어서 웹 브라우저에 올려놓는다고 이미지가 업로드 되도록 할 수는 없다는 얘기다. 앞서 언급한 것처럼 클라이언트의 자원에는 접근할 수 없기 때문이다. 이런 부분은 역시 클라이언트의 자원에 접근하는 일이기 때문에 액티브X든 자바 애플릿이든 다른 기술을 사용해야한다.



동적으로 로딩 하는 트리 만들기



이번에는 좁은 의미의 Ajax인 비동기 통신까지 활용하는 사례를 살펴보자. 트리 컨트롤은 이미 꽤 오래 전부터 액티브X가 아니라 자바스크립트로 구현되어 왔다. 하지만 기존에는 그냥 데이터를 미리 다 로딩해 놓고 보여주는 식이었다. 하지만 Ajax의 등장으로 좀 더 동적인 트리가 가능해졌다. 윈도우에서 탐색기를 열면 처음부터 다 보여주는 것이 아니라 디스크에서 읽은 만큼만 보여주고 다 읽기 전에도 트리를 이용할 수 있다. 이와 같은 동작이 웹에서도 가능하다.


트리 만들기


먼저 트리부터 만들어보자. 이번에도 프로토타입을 사용한다. 다음과 같은 코드로 최상위 노드로 구성된 트리를 그릴 수 있을 것이다.
일종의 웹사이트 분류 트리를 예제 데이터로 사용해보자. 그리고 각 노드의 하위 노드들도 다음과 같이 샘플 데이터를 만든다.

<리스트 8> 최상위 트리

<ul id="tree">
<li id="search"><span>+ </span>검색</li>
<li id="community"><span>+ </span>커뮤니티</li>
<li id="shopping"><span>+ </span>쇼핑</li>
</ul>

<리스트 9> 트리의 하위 노드

<script type="text/javascript">
var treeData = {
search: [{key:'google', name: '구글'}, {key:'naver', name: '네이버'}, {key:'yahoo', name: '야후'}],
community: [{key:'cyworld', name: '싸이월드'}, {key:'daumcafe', name: '다음 카페'}],
shopping: [{key:'openmarket', name: '오픈 마켓'}, {key:'shoppingmall', name: '쇼핑몰'}],
openmarket: [{key:'gmarket', name: 'G 마켓'}, {key:'auction', name: '옥션'}, {key:'ebay', name: '이베이'}],
shoppingmall: [{key:'cjmall', name: 'CJ Mall'}, {key:'amazon', name: '아마존'}]
}
</script>

treeData 변수에 값들을 넣고 있는데 여기서 사용한 문법이 JSON(JavaScript Object Notation)이다. {}로 둘러싼 부분은 하나의 객체가 되는데 사전과 같은 구조다. {key:’google’, name: ‘구글’}은 key, name이라는 두 개의 속성을 가지며 그 값은 각각 ‘google’과 ‘구글’이 되는 것이다. 자바스크립트는 객체 자체가 다른 언어의 사전 객체나 해시 테이블, 맵 등과 비슷한 역할을 한다. Ajax에서 응답의 자료구조로 흔히 쓰이는 형식이다.

treeData의 내용은 트리 구조를 테이블 형태로 풀어 놓은 것이다. 검색 카테고리는 search라는 id를 갖고 있고 이 아래에는 id가 google, naver, yahoo인 항목들이 있다. 마찬가지로 commu nity도 있고 shopping은 한 단계가 더 있는데 openmarket이 있고 shoppingmall이 있는데 각각은 또 하위 엘리먼트를 가진다.


트리 펼치기와 접기


그리고 이제 li 엘리먼트 안에 +가 그려진 span 엘리먼트에 클릭 이벤트를 달면 된다. 다음처럼 달면 된다.

$$(‘#tree span’).each(function(element) {
Event.observe(element, ‘click’, toggle)
})

이 코드는 프로토타입이 제공하는 라이브러리를 사용한 것이다. id가 tree인 엘리먼트 밑에 있는 엘리먼트 중 span 엘리먼트를 모두 찾아서 그 각각에 대해서 이벤트를 할당하는 것이다. Event.observe의 의미는 element에 ‘click’ 이벤트가 발생했을 때 toggle 함수를 실행하라는 뜻이다. toggle은 트리의 하위 노드가 접혀 있으면 펼치고, 펼쳐져 있으면 접는 기능을 하면 된다. 이런 기능은 <리스트 10>이면 충분하다.

<리스트 10> toggle 이벤트

function toggle(event) {
var element = Event.element(event).parentNode
if (element.expanded) {
fold(element)
element.expanded = false
} else {
expand(element)
element.expanded = true
}
}

이제 펼치는 expand 함수와 접는 fold 함수만 남았다. expand는 <리스트 11>과 같다.

<리스트 11> expand 함수

function expand(element) {
var ul = document.createElement('ul')
element.appendChild(ul)
var children = treeData[element.id]
children.each(function(child) {
var li = document.createElement('li')
ul.appendChild(li)
li.id = child.key
li.innerHTML = '<span>+ </span>' + child.name
Event.observe(li.firstChild, ‘click’, toggle)
})
element.firstChild.innerHTML = '- '
}

데이터를 가져와서 document.createElement 함수로 엘리먼트를 생성한 후 현재 노드에 appendChild로 갖다 붙이면 된다. 접는 것은 간단하다. 다음과 같이 expand에서 붙였던 엘리먼트들을 죄다 지우면 된다.

function fold(element) {
element.removeChild(element.getElementsByTagName (‘ul’)[0])
element.firstChild.innerHTML = ‘+ ’
}


동적 로딩 기능 만들기


여기까지만 하면 데이터를 처음에 다 가져와서 트리 구조로 표현하는 것이 되는 것이다. 이제 동적으로 데이터 로딩하는 부분을 추가해보자. expand 함수를 보면 중간에 treeDataelement. id를 통해서 자식 노드들을 배열 형태로 가져오는 코드가 있다. 이 부분에서 Ajax를 통해서 데이터를 가져오게 하면 된다. 다만 비동기로 통신하는 코드는 데이터를 가져오도록 요청하는 부분과 가져온 데이터를 처리하는 부분이 분리가 되어야 한다. expand 함수에서는 데이터를 가져오도록 요청만 하고 데이터를 가져오고 나면 addChildren 함수를 호출되도록 하면 된다. 그럼 <리스트 12>와 같은 코드가 될 것이다.

<리스트 12> addChildren 함수를 이용한 동적 로딩 코드

function expand(element) {
new Ajax.Request('/treeData?id=' + element.id, {
method: get,
onSuccess: addChildren
})
}

function addChildren(request) {
var children = eval(request.responseText)
var ul = document.createElement('ul')
children.each(function(child) {
var li = document.createElement('li')
ul.appendChild(li)
li.id = child.key
li.innerHTML = '<span>+ </span>' + child.name
Event.observe(li.firstChild, 'click', toggle)
})
element.firstChild.innerHTML = '- '
}

Ajax.Request도 프로토타입에서 제공하는 기능이다. 브라우저별로 호출방법이 다른 XMLHttpReqeust 객체를 wrapping 해서 쓰기 편하게 해준다. 여기서 첫 번째 인자가 호출할 URL인데 이 코드가 제대로 동작하려면 서버 사이드의 코드도 필요하다. 하지만 간단하게 텍스트 파일을 만들어서 텍스트 파일 URL을 써 넣고 텍스트 파일 내용에는 JSON 타입으로 내용을 써 놓아서 테스트해볼 수는 있을 것이다.



재미 있는 Ajax UI 프로그래밍



몇 가지 예제를 통해 살펴본 것처럼 웹에서 자바스크립트로 기능성 높은 UI를 만드는 일은 그리 어려운 일이 아니다. 게다가 이미 이런 UI 컴포넌트들을 만들어서 모아 놓은 오픈소스 라이브러리도 있다. Dojo같은 프레임워크는 윈도우 프로그래밍 하는 느낌으로 사용할 수 있고 script.aculo.us 같은 프레임워크는 UI 관련된 컴포넌트들을 모아서 제공하고 있다. jQuery는 앞서 살펴본 프로토타입과 script.aculo.us를 합친 기능을 제공한다. 대부분 커스터마이즈하기 쉽게 되어 있기 때문에 별다른 노력 없이 가져다 쓸 수 있을 것이다.
그럼에도 불구하고 이런 UI 컴포넌트를 만드는 방법은 알아둘 필요가 있다. 프레임워크들이 제공하는 것과 비슷하지만 다른 컴포넌트를 만들어야 하는 경우가 많기 때문이다. 예를 들면 스프링노트에서는 트리 목록을 보여주는 사이드 바가 있는데 이 부분의 크기를 마우스 드래그로 조정하는 기능이 있다. 이런 기능은 일반적인 드래그 앤 드롭 컴포넌트로는 붙이기 어렵다. 그럴 때 드래그 앤 드롭의 구현 방법을 알고 있다면 손쉽게 구현할 수 있다.

이미 완성된 컴포넌트를 조립하는 액티브X가 개발이 빠른 측면도 있지만 배포 면에서는 아무래도 개발자나 사용자나 모두에게 불편하다. 반면에 Ajax UI 컴포넌트는 개발이 액티브X에 비해 크게 느리지 않고 배포에는 월등히 유리하다. 접근성의 측면에서도 액티브X나 플래시, 자바 애플릿 등의 다른 기술에 비해 브라우저만 있으면 되는 Ajax 기술이 가장 좋다고 할 수 있다.

하지만 필자가 그보다 더 중요한 장점으로 꼽고 싶은 것은 Ajax 개발은 좀 더 재미있다는 것이다. 자바스크립트는 현재 널리 사용되는 언어 중 가장 다이내믹한 언어이다. 그 덕분에 코드와 생각의 거리가 비교적 짧아서 좋은 코드를 만들 수 있고 다른 UI 개발에 비해 결과를 빨리 확인할 수 있다는 점도 매력적이다. 자바스크립트가 한때는 스파게티 소스의 온상인 것처럼 이야기되기도 했지만 프로그래머가 아닌 사람들도 쉽게 가져다 쓰고 응용할 수 있는 언어임은 부인할 수 없을 것이다. 그리고 동적인 언어는 지저분해질 수 있는 한계도 크지만 더 좋은 코드를 만들 수 있는 한계도 크다. 개발자에게 더 많은 권한을 부여해주는 것이다.

자바스크립트와 CSS 기술의 발전 덕분에 이제 UI 때문에 액티브X를 쓸 필요는 거의 없어졌다. 그럼에도 불구하고 아직 그룹웨어나 SI 분야에서는 액티브X가 많이 쓰이고 있다. 웹 접근성도 해치고 사용자도 귀찮게 만드는 액티브X, UI에서 사용하는 것만이라도 줄여야 하지 않을까. 필자가 이 글을 쓰고 있는 PC의 OS도 리눅스인데 액티브X 때문에 사내 그룹웨어에 접속도 못하는 불편을 겪고 있다. 최근 PC를 새로 구입한 사람들도 윈도우 비스타에서 제대로 사용할 수 없는 사이트가 많아서 어려움을 겪고 있을 것이다. 말도 많고 탈도 많은 액티브X, UI 부분만이라도 Ajax로 대체해 보자. Ajax 프로그래밍이 생각보다 쉽고 재미있다는 것을 독자 여러분도 느끼게 되기를 바란다.



자바스크립트 개발 도구



자바스크립트에도 다양한 프레임워크와 개발도구가 있는데 여기서는 프로토타입(prototype)과 파이어버그(Firebug)를 소개해보려고 한다. 프로토타입은 요즘 가장 널리 쓰이는 자바스크립트 프레임워크이다. Ajax 애플리케이션에서 많이 사용하는 패턴 중 언어의 기본적인 부분들을 쉽게 해주는 라이브러리이다. 대표적으로 $() 함수가 있다. 앞선 예제에서 사용했던 코드 중에 다음과 같은 부분은

document.getElementById(‘editor’)

아래와 같이 바꿀 수 있다.

$(‘editor’)

단순한 축약에 불과하지만 실제 개발 과정에서는 정말 큰 편의를 제공한다. 이외에 CSS Selector와 같은 문법으로 엘리먼트의 목록을 가져올 수 있는 $$ 함수도 있고 Event 객체나 Array 객체를 편리하게 다룰 수 있는 방법도 제공한다. 예제를 통해 살펴보자.

var list = [1, 4, 6, 3, 11, 8, 4, 7, 8]
var uniqList = list.uniq() // 중복 원소 제거 [1, 4, 6, 3, 11, 8, 7]

루비에서 많이 사용하는 방식으로 closure를 활용해서 for 루프를 대체할 수도 있다.

list.each(function(element) {
sum = sum + element
})

Event 객체의 경우는 브라우저 호환성을 걱정하지 않고 사용할 수 있는 도구들을 많이 제공한다.

Event.pointerX(event) // IE, 파이어폭스에 상관없이 event가 발생한 마우스 좌표를 가져오면서 스크롤 보정도 해준다.
Event.element(event) // IE의 event.srcElement, Firefox의 event.targetElement

자세한 내용은 prototypejs.org에서 볼 수 있다. 요즘은 jQuery라는 프레임워크도 뜨고 있지만 프로토타입이 워낙 빠른 속도로 대중화되어서 많은 다른 라이브러리가 프로토타입 기반으로 되어 있기 때문에 알아두면 좋을 것이다.

일반적으로 복잡한 Ajax 애플리케이션을 개발할 때는 디버깅 도구가 좀 더 많고 표준 지원이 좋은 파이어폭스 기준으로 개발하는 경우가 많다. 파이어폭스에는 자바스크립트 콘솔, Web Developer 툴바, DOM Inspector 등 개발에 편리한 도구가 많다. 그 중 가장 중요한 도구는 아마 파이어버그일 것이다. DOM 구조를 실시간으로 보면서 각종 속성들을 쉽게 볼 수 있고 내용을 바꿀 수도 있기 때문이다. 자바스크립트도 자동완성이 지원되는 shell이 제공되며 간단한 디버깅도 가능하고 에러가 났을 때 정확한 지점을 표시해 주기도 한다. IE에도 최근 MS가 내놓은 IE Dev Toolbar가 있긴 하지만 파이어버그의 기능이 압도적이다.

파이어버그의 DOM Inspector

Return top