간만에 tool for tools에 포스팅을 한다. 이번 검색 컨텐츠 개편 작업을 하면서 한개 탭을 ajax 기반으로 구축하기로 작정하고 어느정도 구현이 된 상태다. ajax 기반 작업은 추천검색창, dw top 실시간 검색어 에 이미 적용을 해 본 상태지만, 그 때는 소량의 한정된 데이터를 전송받아서 출력에 사용했었다. 이번은 이전과는 다르게 대량의 XML 데이터가 전송된다. 그에 따른 예전에는 경험하지 못한 문제들에 봉착하게 되었고, 그 문제들을 하나 하나 어렵게 해결해 가는 과정을 거치면서 한번 정리할 필요성을 느껴 오늘 포스팅을 하게 되었다. 실은 dw top 실시간 검색어 작업을 마치고 나서 위키에 정리해서 포스팅을 하려 했으나 몸이 많이 지친 상태라 잠깐 쉬자 하다가 결국 때를 놓쳐 버렸다. 물론 이렇게 블로그에 끄적 거릴만한 주제가 아니긴 하지만 일단 여기다 생각나는대로 조금씩 정리해서 나중에 위키로 옮기기로 하자.
AJAX (Asynchronous JavaScript and XML). 이거 요즘 많이들 얘기한다. 아약스, 아작스, 에이작스 등등으로 불리는데, 많은 이들이 에이잭스 라고 발음 한다. 뭐 꼴리는대로 읽어도 뭐라 그럴 사람 없는 것 같다. 아무튼 이걸 이용하면 웹페이지를 새로 받아오지 않고서도 서버에서 데이터를 받아와 웹페이지의 특정 내용을 갱신하는 것이 가능하다. 쉽게 예를 들면 추천 검색창이나 구글 맵 등과 같은 것이 ajax를 활용한 예라고 할 수 있겠다. 이 ajax 란 것, 아니 좀 더 구체적으로 얘기 하자면 XML HTTP Request 란 것이 언제부터 웹브라우저에 껴 들어오게 됐는지는 모르겠으나 현재는 거의 De Facto가 되다시피 해서 이를 지원하지 않는 브라우저는 거의 없는 듯 하다. 문제는 브라우저 별로 이 ajax를 구현한 방식이 조금씩 달라서(심지어 버전별로도!!!) 브라우저 별로 약간의 조건을 걸어주는 루틴이 필요하긴 하지만서도 점점 통일이 되어가는 것 같으니 앞으론 큰 문제가 아닐 것 같다.
자, ajax가 뭘 어떻게 하길래 웹페이지를 새로 받아오지 않고서도 특정 내용을 갱신할 수 있는지 좀 더 구체적으로 보자. 결국은 이렇게 요약 가능할 것 같다. XML HTTP Request, JavaScript, CSS(Cascading Style Sheet), DOM(Document Object Model). XML HTTP Request 저 이상한거 말고는 뭐, 다 아는 거라고? 그렇다. ajax는 알고보면 전혀 새로울 것이라곤 없는 것들의 짬뽕인 것이다. 그나마 생소한 저 XML HTTP Request 조차도 일단 내가 확인한 동작 가능한 브라우저가 IE5 였으니 이것들이 브라우저에 들어간지는 상당히 오래됐다.
하나씩 보자.
XML HTTP Request :
가장 핵심이라고 할 수 있다. 이름에서 알 수 있듯이 XML하고 연관이 좀 있다. 일종의 javascript 객체로서 각 브라우저별로 객체를 생성하는 방법이 조금씩 다르다.
var xmlhttp=false; /*@cc_on @*/ /*@if (@_jscript_version >= 5) // JScript gives us Conditional compilation, we can cope with old IE versions. // and security blocked creation of the objects. try { xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (E) { xmlhttp = false; } } @end @*/ if (!xmlhttp && typeof XMLHttpRequest!='undefined') { try { xmlhttp = new XMLHttpRequest(); } catch (e) { xmlhttp=false; } } if (!xmlhttp && window.createRequest) { try { xmlhttp = window.createRequest(); } catch (e) { xmlhttp=false; } }
이 것이 하는 일은 백그라운드로(어렵게 말해 asynchronous 비동기적으로) 웹서버를 호출해서 그 결과를 XML로 받아오는 역할을 한다.
JavaScript :
물론 아시겠지만 Java와 JavaScript는 별개의 것이다. ajax 작업을 하게 되면 가장 큰 비중을 차지하게 되는 것이 바로 이 javascript다. XML HTTP Request 가 넘겨준 XML 데이터를 지지고 볶아서 웹페이지의 특정 내용을 갱신하는 역할을 한다. javascript로 객체지향 프로그래밍을 할 수 있다는 것을 아시는지? 클래스를 생성하고 멤버 변수와 멤버 함수들을 이용할 수 있다. 물론 정통 객체지향 언어인 C++이나 Java 처럼 객체 상속이라든지 메서드 오버라이드와 같은 것은 불가능 하지만 저것 만으로도 상당히 깔끔한 객체지향 프로그래밍을 할 수 있다. 받아온 XML 데이터를 지지고 볶느라 기존의 cgi 클라이언트와는 비교할 수 없을 정도로 상당한 분량의 javascript 코드가 들어가게 되는데 객체지향으로 코딩을 하면 조금이나마 깔끔한 코드를 만들 수 있다.
아래는 위에서 생성한 javascript 객체로 웹서버를 호출해서 데이터를 받아오는 루틴이다.
xmlhttp.open("GET", "/test.txt", true); xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState==4) { alert(xmlhttp.responseText) } } xmlhttp.send(null)
위의 코드에서도 보다시피 실제 받아오는 데이터가 꼭 XML일 필요는 없다. responseText 속성은 텍스트 형식의 데이터를 돌려준다. 즉 웹페이지 내용을 갱신하는데 사용가능한 html 코드를 넘겨줘도 무방하다는 얘기다. 만약 웹서버의 test.txt의 내용이 well-formed XML 문서일 경우 responseXML 속성엔 XML DOM 객체가 들어 있게 된다. well-formed XML 문서가 아닌 일반 텍스트일 경우 responseXML 의 DOM 객체는 아무 자식노드도 갖고 있지 않은 널 트리의 DOM 객체이다.
DOM :
위에서 javascript로 XML 데이터를 지지고 볶는다고 했는데, 그 지지고 볶는 도구가 바로 DOM이다. DOM은 현재 웹페이지의 HTML 구조를 XML 트리형식으로 관리를 한다. 이를 이용해 특정 노드의 값을, 서버에서 받아온 DOM 객체의 값으로 수정, 대체가 가능하다. 즉 웹페이지의 특정 내용을 변경 시키는 것이다.
이름 : <span id="contents">바보</span> var objContents = document.getElementById("contents"); objContents.innerHTML = "똥개";
이 페이지가 실행되면 이름 : 바보 가 페이지 리로드 없이 이름 : 똥개 로 바뀌게 된다.
CSS :
이러한 일련의 동작들을 예쁘게 꾸며주는 역할을 한다. 디자이너가 아니라면 크게 신경쓰지 않아도 무방하다.
이것들을 짬뽕해서 간단히 동작하게 만든 test.html 이다.
이름 : <span id="name">똥개</span> var xmlhttp=false; /*@cc_on @*/ /*@if (@_jscript_version >= 5) // JScript gives us Conditional compilation, we can cope with old IE versions. // and security blocked creation of the objects. try { xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (E) { xmlhttp = false; } } @end @*/ if (!xmlhttp && typeof XMLHttpRequest!='undefined') { try { xmlhttp = new XMLHttpRequest(); } catch (e) { xmlhttp=false; } } if (!xmlhttp && window.createRequest) { try { xmlhttp = window.createRequest(); } catch (e) { xmlhttp=false; } } xmlhttp.open("GET", "/cgi-bin/name.cgi", true); xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState==4) { var objName = document.getElementById("name"); objName.innerHTML = xmlhttp.responseText; } } xmlhttp.send(null)
자, 대충 이게 어떻게 구성이 되어 동작하는지를 알아 봤다. 하지만 어떤 것이든 만만한 것은 없다.
이제부터는 이 ajax를 이용해서 웹페이지를 맹글때 발생하는 지랄같은 것들에 대해 살펴본다.
가장 먼저 맞닥뜨리게 되는 것이 바로 보안 문제이다. cross domain 문제라고도 하는데, xmlhttp.open 메서드는 같은 도메인의 같은 서버에 대해서만 동작한다. 즉 위의 test.html이 a.dreamwiz.com 에 들어있다면 xmlhttp.open(“GET”, “http://b.dreamwiz.com/name.txt”, true); 를 호출하게 되면 브라우저는 권한 문제가 어쩌구저쩌구 하는 에러를 내면서 스크립트 실행을 멈춘다. 이는 브라우저의 보안 설정과 관련이 있는데 브라우저의 옵션중에 보안설정을 열어서 해당 도메인, 위의 경우는 b.dreamwiz.com 을 신뢰하는 도메인 리스트에 사용자가 직접 추가하는 것 말고는 피해갈 수 있는 방법이 없다. IE의 경우는 개구멍이 있는데 바로 iframe을 이용하는 것이다. 즉 b.dreamwiz.com에 xmlhttp 관련된 스크립트를 담고 있는 iframe.html을 작성하고 a.dreamwiz.com/test.html 에서 iframe으로 b.dreamwiz.com/iframe.html 을 인클루드 하는 것이다. 이렇게 하면 일단 보안 문제는 비껴갈 수 있다. 하지만 이 경우 두 서버, 즉 a와 b서버간의 스크립트 데이터가 공유가 안된다. 이를 해결하기 위해서 a 서버의 test.html과 b 서버의 iframe.html의 스크립트에 document.domain=”dreamwiz.com”; 이라고 상위 도메인을 명시해 줌으로써 같은 도메인 내의 서버간에 javascript 데이터 공유를 가능하게 할 수 있다. 하지만 이 방법은 어디까지나 IE용 개구멍이다. mozilla 기반의 브라우저 들에서는 iframe 내의 객체를 억세스 할 수 있는 방법이 없다.
가만 보면 IE는 보안이 참 느슨하다. 하지만 이는 역으로 개발자에게는 꽤나 달콤한 유혹이 된다. 일단은 돌아가는데 뭐가 문제인가, IE에서 잘 도는데 모질라 따위는 무시하지. 이렇게 되면서 점점 표준과는 멀어지는 웹페이지들을 만들게 된다. 학교 시절엔 개구멍이 무척 유용하긴 하다. 하지만 개구멍은 어디까지나 개구멍인 것이다.
이를 극복하기 위한 아이디어중 아주 기발한 것이 있다. 여기 http://blogs.ebusiness-apps.com/dave/?p=92 를 참고해 보기 바란다. 간략히 설명하자면, cgi가 단순히 xml 데이터를 되돌려 주는게 아니라 xml 객체를 담고 있는 자바스크립트를 리턴해 준다! 일종의 RPC를 구현한 것이다.
다음은 DOM으로 웹페이지 구조를 관리하느라 그런지 구조가 복잡한 웹페이지일 경우 사용자 액션에 대한 반응이 좀 느린 경우가 있다. 이는 특히나 플래시가 많이 들어있고 테이블이 복잡하게 얽혀 있는 탑페이지의 경우 특히 심한데 이는 나날히 발전하는 개인용 pc의 컴퓨팅 파워로 쉽게 극복가능하리라 생각된다.
마지막으로 서버가 보내주는 html 태그를 단순히 출력만하던 멍텅구리 브라우저가 ajax로 인해 리치 클라이언트가 됨에 따라 cgi 클라이언트의 javascript 코딩이 무척 복잡하고 방대해졌다. 아주 제길쓴이다. DOM 객체를 파싱하기 위해 XML 파서가 동원되고 스크립트 코드 안에 html 코드들이 짜깁기 되는게 아주 난장판이다. 이를 피하기 위해 받아온 DOM 객체에 바로 xslt 를 적용해 html로 포맷팅이 가능하지만, xml의 cdata가 escape 되서 포맷팅되기 때문에 html 태그로서의 기능을 상실해 버리는 문제점이 있다. 다른 대안으로 ajax 프레임 워크를 만들어서 간략화 하기 위한 프로젝트들이 한창이다.
간략하게나마 ajax에 관련된 사항들을 정리해 봤다. 브라우저의 보안관련 문제라든가 xslt의 포맷팅 문제는 아직도 테스트를 더 해봐야 하고 자료를 찾아서 공부를 더 해야한다. 앞으로 ajax가 웹어플리케이션에 있어 클라이언트측의 대세가 될 것은 분명하다. 기존의 서버측 cgi에서 데이터를 처리해 html까지 생성해서 클라이언트에 전송하던 방식에서, 이제 서버측은 데이터만 생성하고 클라이언트 측이 뷰를 담당하는 구조로 되므로, 서버측 cgi는 오히려 단순 명료해 진다.
어플리케이션의 활용 구분에 있어 알랜 쿠퍼(Alan Cooper)라는 사람은 몇가지로 분류를 했다. 그 중 트랜지언트(transient) 패턴과 소브린(sovereign) 패턴이란 것이 있다. 소브린 패턴은 지속적인, 즉, 개발자들이 몇시간씩 머리 싸메고 들여다보는 vi와 같은 에디터나 기획자가 몇시간씩 끙끙대는 파워포인트 류가 되겠다. 반면 트랜지언트 패턴은 일회적인, 즉, 파일관리자나 메신저, 웹브라우저 같은 것들이 이 부류가 되겠다. ajax가 대중화 됨에 따라 대표적인 소브린 패턴에 해당하는 업무용 어플리케이션들이 점점 웹 어플리케이션으로 되고 있다(일전의 구글리더에 관한 포스팅 참조). 즉 웹브라우저 자체가 점점 소브린 패턴쪽으로 이동하고 있는 것이다. 더 나아가 웹페이지에서 이와같은 사용자 상호작용이 가능하게 되면 웹 OS가 아예 불가능한 얘기가 아니다. 실제로 웹데스크탑을 구현하는 프로젝트가 진행중이다. 정말 어느날 갑자기 구글 OS 등장이요, 이런 소식이 들릴날이 올 지도… 흥미진진한 미래가 기대반 두려움 반이다.
이 포스팅은 생각나는 대로 계속 추가해서 어느정도 정리되면 위키로 옮길 작정이다. 예전의 추천 검색창 관련 포스팅도 그랬듯이 어느날 갑자기 없어지더라도 그러려니 하시길.
reference :
XML HTTP Request 관련 예제 스크립트 : http://jibbering.com/2002/4/httprequest.html
추천 검색창 관련 나의 작업 내용 : 이 링크는 외부에 노출되면 문제 발생 소지가 있어 링크를 뺀다. 나중에 이 포스팅을 위키로 옮기면 추가할 것이다.
JSON 프로젝트 : http://json-rpc.org/
웹데스크탑 프로젝트 : http://www.x-desktop.org/
어플리케이션 활용 패턴에 관한 알랜 쿠퍼의 글 : http://www.cooper.com/articles/art_your_programs_posture.htm
cross domain 문제를 극복할 수 있는 아이디어 : http://blogs.ebusiness-apps.com/dave/?p=92
위 포스팅과 관련된 JSONP(JSON with Padding) : http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/