블로그 이미지

카테고리

데꾸벅 (194)
Publisher (39)
Scripter (97)
AJAX (6)
COMET (1)
JAVASCRIPT (34)
EXTJS (50)
jQuery (5)
Prototype.js (1)
Programmer (1)
Designer (30)
Integrator (18)
Pattern Searcher (4)
News (2)
강좌 및 번역 (3)

최근에 올라온 글

최근에 달린 댓글

Ajax Framework 분석

Scripter/AJAX / 2009. 5. 4. 11:45


ajax framework 선택시 Matt Raible씨가 그의 고객에게 했던 분석을 그의 블로그에 포스팅 하였다. 항상 ajax framework선택시 어떠한 프레임웍을 선택할지 고민이 많이 된다. 재미있는것은 ajaxian 포스팅에 보면 다트판을 그려놓고 선택한다는 농담이.. ㅡ.,ㅡ;

여러분도 이 다트판을 돌려 선택하나요? 사실 일전에 데꾸벅이 포스팅한 ajax framework고르는법 글도 사내 프로젝트에 사용할 프레임웍을 고민하다 만든 포스팅이였는데 같은 고민을 하는데도 질이 틀리네요.. 한번씩 참조하시는것도 괜찮을듯 싶어 포스팅합니다.

아래표는 Matt Raible씨가 모든 프레임웍으로 직접 프로토타입형태의 애플리케이션을 만들어보고 중요한 기준이 되는 특징들로 도식화한 것입니다.

참조 : ajaxian, indeed, google doc




아래 그래프는 결론적으로 모든 ajax framework들이 웹애플리케이션을 만들기에 손색이 없었다고 합니다. 거의 대부분의 프레임웍들이 이제는 거의 비슷하게 성능평가가 나오고 있습니다. 정말로 다트판을 던져 고른다는 농담이 그냥 나온 얘기는 아니였네요.

아래는 각각의 프레임워크를 동일 가중치를 주어 평가한 그래프입니다.



여기서 비교된 프레임워크는 가장 인기있는 프레임웍을 대상으로 했다고는 하지만 jquery는 빠져있네요.

 

indeed 에서 검색한 결과 위 그래프와 같이 jQuery가 짧은 기간에 급상승하고 있네요.. 여기서는 dojo, yui, gwt, extjs, jquery,prototype.js까지 비교하고 있습니다. qooxdoo나 mootools는 빠져있네요 ㅡ.,ㅡ;


대부분의 ajax framework들이 prgressive enhancement하다고 합니다.  점점더 많은 요구와 사용자 committer들, 다양한 플러그인들, 각 프레임웍마다의 기능들을 서로 보완해 나가고 있습니다.
얼만전 ext3.0 core beta에서와 마찬가지로 서로의 장단점들을 보완해 가며 서서히 하나의 완전한 프레임웤들로 자리매김 하고 있네요~

하나의 프레임웍만 제대로 익힌다면 다음 프레임웍은 떡 주물듯이~ 가능할까요?









'Scripter > AJAX' 카테고리의 다른 글

CSSHttpRequest  (0) 2008.11.12
HTTP Header에 대하여  (0) 2008.11.08
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
100라인 Ajax Wrapper  (0) 2008.04.19
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |

CSSHttpRequest

Scripter/AJAX / 2008. 11. 12. 15:11

XML과 XMLHttpRequest(XHR)의 시대는 물건너 갔는가?
JSON(JSONML)과 CSS만으로 통신을 하다 못해 이제는 CSSHttpRequest까지 나왔다.

CSS의 Hack을 이용한 것으로 cross-domain이 가능하다.
CSSHttpRequest(CHR)은 CSS 통신을 위한 크로스도메인 AJAX 메쏘드이다.

CSSHttpRequest.js
(function(){
    var chr = window.CSSHttpRequest = {};
   
    chr.ID = "__csshttprequest__";
    chr.DELAY = "50";
    chr.counter = 0;
    chr.requests = {};
    chr.timeout = null;
    chr.styleSheet = null;
    chr.styleElement = null;
   
   
    chr.get = function(url, callback) {
        var id = (++chr.counter);
        var s = chr.getStyleSheet();
        var item, index;
       
        // IE
        if(s.addImport) {
            index = s.addImport(url);
            item = s.imports(index);
        }
       
        // W3C
        else if(s.insertRule) {
            index = s.insertRule("@import url(" + url + ");", 0);
            item = s.cssRules[index];
        }
       
        chr.startPoll();
        return chr.requests[id] = {
            id: id,
            index: index,
            item: item,
            styleSheet: item.styleSheet || item,
            callback: callback
        };
    };
       
   
    chr.getStyleSheet = function() {
        if(chr.styleSheet)
            return chr.styleSheet;
       
        var s, e;
        if(document.createStyleSheet) {
            s = document.createStyleSheet();
            e = s.owningElement || s.ownerNode;
        } else {
            e = document.createElement("style");
            s = e.sheet;
        }
       
        e.setAttribute("type", "text/css");
        e.setAttribute("media", "print, csshttprequest");
        e.setAttribute("title", chr.ID);
        e.setAttribute("id", chr.ID);
       
        if(!e.parentNode)
            document.getElementsByTagName("head")[0].appendChild(e);
        if(!s)
            s = e.sheet;
       
        chr.styleSheet = s;
        chr.styleElement = e;
       
        return s;
    };
   
   
    chr.reset = function() {
        var s = chr.getStyleSheet();
        if(s.imports)
            while(s.imports.length)
                s.removeImport(0);
        else if(s.cssRules)
            while(s.cssRules.length)
                s.deleteRule(0);
        chr.requests = {};
    };
   
   
    chr.startPoll = function() {
        if(chr.timeout)
            return;
        chr.timeout = window.setTimeout(chr.poll, chr.DELAY);
    };
   
   
    chr.poll = function() {
        chr.timeout = null;
        var retry = false;
       
        for(var id in chr.requests) {
            var request = chr.requests[id];
            if(id != request.id)
                continue;
            var response = chr.parse(request.item.styleSheet || request.item);
            if(response === undefined)
                retry = true;
            else {
                delete chr.requests[id];
                request.callback(response, request);
            }
        }
       
        if(retry)
            chr.startPoll();
        else
            chr.reset();
    };
   
   
    chr.parse = function(s) {
        if(!s)
            return;
       
        var res = "";
        var d = decodeURIComponent;
       
        // IE
        if(s.imports && s.imports.length) {
            for(var i = 0; i < s.imports.length; i++) {
                var m = s.imports(i).href.match(/about\:chr\:(.+)/);
                if(m && m[1])
                    res += m[1];
            }
            return d(res);
        }
       
        // W3C
        else if(s.cssRules && s.cssRules.length) {           
            var rs = s.cssRules;
            for(var i = 0; i < rs.length; i++) {
                var r = rs[i];
                if(r.type != CSSRule.IMPORT_RULE)
                    continue;
                var m = r.cssText.match(/@import\s+url\("?about\:chr\:([^")]+)"?\)/);
                if(m && m[1])
                    res += m[1];
            }
            return d(res);
        }
    };
})();



XHR이 XML이나 JSON을 타겟으로 삼는다면 CHR은 CSS파일의 @import url()을 이용한다.
CSSJSON 이나 CSSML을 이용한것이 사실 XML/XSLT를 이용한것 보다 화면레더링하기는 사실 더 편하고 관리하기도 쉽다. CSS,JSON을 이용한다면 XML Node찾는것 보다 더 쉽지 않을까?


CHR을 이용하려면 AJAX Wrapper인 CSSHttpRequest.js 을 받아서 아래와 같이 입력한다.
    CSSHttpRequest.get(
        http://www.nb.io/hacks/csshttprequest/hello-world/helloworld.css,
        function(response) { alert(response); }
    );
보는바와 같이 css를 호출한다. 해당 css를 살펴보면
 @import url(about:chr:Hello%20World!);
와 같이 CSS의 about: 을 이용한다.

CHR은 XHR이나 JSONP와 같은 sampe-origin policy (같은 도메인에서 동작)과 다른 범주에 속한다. (흠..번역이..쩝..) JSONP와 같이 CHR도 GET Reqeust밖에 사용하지 못한다.


샘플 :


참조 사이트 :
http://nb.io/hacks/csshttprequest/



'Scripter > AJAX' 카테고리의 다른 글

Ajax Framework 분석  (0) 2009.05.04
HTTP Header에 대하여  (0) 2008.11.08
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
100라인 Ajax Wrapper  (0) 2008.04.19
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |

http://www.p3pwriter.com/LRN_111.asp



HTTP Header
해당 웹페이지가 보내지기 전에 META Information을 포함하여  페이지에 대한 정보 및 브라우저에 전달하는 정보

HTTP/1.1 200 OK
Date: Wed, 11 Oct 2003 01:11:50 GMT
Server: Apache/1.2.0
Last-Modified: Fri, 01 Jun 2003 11:16:44 GMT
ETag: "31f9e-620-44ca89c1"
Content-Length: 3112
Accept-Ranges: bytes
Connection: close
Content-Type: text/html


META HTTP-EQIV
웹페이지 화면 상단에 위치한 META태그로 해당 페이지 전체에 적용된다.
아래와 같이 입력시

<META HTTP-EQUIV="Author" CONTENT="P3Pwriter">

브라우저에서는 다음과 같은 Header정보를 보여준다.

HTTP/1.1 200 OK
Date: Wed, 11 Oct 2003 01:11:50 GMT
Server: Apache/1.2.0
Last-Modified: Fri, 01 Jun 2003 11:16:44 GMT
ETag: "31f9e-620-44ca89c1"
Content-Length: 3112
Accept-Ranges: bytes
Author: P3Pwriter
Connection: close
Content-Type: text/html

브라우저가 웹페이지문서를 받기전에 해당페이지에 Header값이 전달된다.
HTML이 브라우저에 의해 decoded되기 될때 브라우저에 Header값이 전달된다는 얘기이다.

HTTP-EQUIV의 정의에서 보면 이것은 "응답 HEADER안의 특별한 문자로 된 정보"를 읽는다는 것이다. 진실은 대부분의 서버가 META tag를 이용하여 페이지에 Header값들을 추가한다는 것이다.  P3P의 경우는 compat policy가 반드시 응답Header에 의해 전달되는것은 별로 좋지 않다.

실제 P3P의 동작원리
P3P Header는 브라우저에 의해 compact policy를 전달되고, 응답Header 안에 포함된 cookie값에 의해 페이지에 전달된다. 전달된 Policy는 브라우저의 privacy 셋팅에 의해 결정지어진다.
HTTP Header는 두가지 다른 방법으로 페이지에 추가될수 있다. 첫번째는 선택된 페이지나 모든 페이지의 header정보를 전달하는 서버파일들을 수정하는 것이고,  두번째는 asp,php,perl,cold funsion과 같은 프로그램언어를 사용하는 방법이다. 

프로그램언어를 이용해서 P3P Header를 추가하기 위해서는 HTML을 렌더링하기 전에 작성해야 한다.

<?여기다가 공백없이 작성해 주세요 UTF-8일때 NOBOM으로도... ?>
<HTML>
<HEAD>
<TITLE>MyPage</TITLE>
</HEAD>
<BODY>
Information
</BODY>
</HTML>






 

'Scripter > AJAX' 카테고리의 다른 글

Ajax Framework 분석  (0) 2009.05.04
CSSHttpRequest  (0) 2008.11.12
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
100라인 Ajax Wrapper  (0) 2008.04.19
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |

location.href =====> protocol :// hostname : port / pathname ? search # hash





히스토리기능으로 고민하던 어느분이 메일로 도움을 청해와서 찾아보던중
Ajax History기능구현이 가능하다는걸 알았다.
관련글을 찾아보던중 번역문서를 찾아 포스팅 한다.

원문 :  [ajax-history-libraries],
번역문 : [한빛미디어]
국내관련글 : juDe's blog(친절히도 설명해 놓았다.. ^^) [1],[2],[3],[4]

관련 파일 : dhtmlHistory.js



===========================================================
여기서는 AJAX 응용 프로그램에 즐겨찾기와 뒤로 이동을 지원하는 오픈 소스 자바스크립트 라이브러리를 소개할 것이다. 이 글을 마지막까지 보게된다면 구글맵스지메일과 같은 곳들조차 제대로 지원하지 못했던 웹 사이트에서의 즐겨찾기, 앞으로 이동, 뒤로 이동과 같은 AJAX의 문제들을 해결할 수 있게 될 것이다.

"AJAX: 즐겨찾기와 뒤로 이동을 다루는 방법"에서는 에 이잭스(AJAX) 응용 프로그램들이 현재 당면하고 있는 즐겨찾기, 뒤로 이동과 같은 중요한 문제들을 설명하고, 이 문제를 해결하기 위한 오픈 소스 프레임워크인 RSH(Really Simple History) 라이브러리를 소개하고, 실제 예제들을 몇 가지 소개할 것이다.

([1]역주: 영어사전에서 AJAX는 에이잭스로 소개되며, 그리스의 신화의 아이아스나 오딧세이의 아이아스를 의미한다. sys-con의 AJAX & Rich Internet Applications 프레젠테이션에서도 그 발음을 확인할 수 있다. 네덜란드 축구팀의 이름을 딴 아약스, 또는 아작스 등으로 불리기도 한다. 한편, 세제 제조회사인 Ajax Industries는 "에이잭스社"로 옮긴다.)

여 기서 소개할 프레임워크의 주요 부분은 두 개로 나뉘어진다. 첫째는 클라이언트의 정보를 갖고 있는 임시 세션 캐시를 사용하기 위해 숨겨진 HTML 폼을 사용한다. 이 캐시는 어떤 지점으로 이동하거나 페이지를 떠나는 경우에도 사용할 수 있다. 둘째는 A 태그와 숨겨진 iframe을 사용해서 브라우저 히스토리를 가로챈 다음 브라우저 히스토리 이벤트를 기록하고, 뒤로 이동과 앞으로 이동 버튼에 연결한다. 이 두 가지 기능은 모두 개발시에 쉽게 이용할 수 있도록 간단한 자바스크립트 라이브러리로 되어있다.

문제

즐 겨찾기와 뒤로 이동은 여러 페이지로 구성된 전형적인 웹 응용 프로그램에서 매우 유용하다. 사용자가 웹 사이트를 항해할 때, 브라우저의 주소 창은 새로운 URL로 업데이트되며, 나중에 이 주소를 다시 방문하기 위해 이메일에 붙여넣거나 즐겨찾기에 추가할 수 있다. 뒤로 이동, 앞으로 이동 버튼들도 올바르게 동작하며, 사용자는 방문했던 페이지들을 자유로이 이동할 수 있다.

그 러나, 에이잭스 응용 프로그램은 웹 페이지 하나에서 동작하는 복잡한 프로그램이기 때문에 예외적인 응용 프로그램이다. 브라우저는 이런 기능을 위해 만들어지지 않았다. 웹 응용 프로그램들이 마우스 클릭을 할 때마다 완전히 새로운 페이지를 가져올 수 있게 되었어도 브라우저는 과거에 사로잡혀 있다.

지메일과 같은 AJAX 소프트웨어에서 사용자가 기능을 선택하고, 응용 프로그램의 상태를 바꾸어도 브라우저의 주소창은 항상 같은 위치만 갖고 있기 때문에 응용 프로그램의 특정 화면에 대해 즐겨찾기를 축가하는 것은 불가능하다. 뿐만 아니라, 사용자가 이전 작업을 원상태로 되돌리기위해 뒤로 이동 버튼을 클릭하면 브라우저가 응용 프로그램의 웹 페이지를 떠나기 때문에 사용자들은 놀라게 된다.

해결책

오픈소스 RSH(Really Simple History) 프레임워크는 이러한 문제를 해결하고, 에이잭스 응용 프로그램에서 즐겨찾기, 뒤로 이동, 앞으로 이동을 제어할 수 있게 해준다. RSH는 현재 베타 버전이며, 파이어폭스 1.0, 넷스케이프 7 이상, 인터넷 익스플로러 6 이상의 버전에서 동작하며, 사파리는 현재 지원되지 않는다.(이에 대한 설명은 나의 웹 로그에서 Coding in Paradise: Safari: No DHTML History Possible를 참고하기 바란다.)

몇몇 에이잭스 프레임워크는 현재 즐겨찾기와 히스토리 문제를 해결하려 하고 있지만 이들 프레임워크는 구현 방식 때문에 몇가지 중요한 버그들을 해결하지 못하고 있다.(보다 자세한 사항은 "Coding in Paradise: AJAX History Libraries"를 참고한다) 게다가, 많은 에이잭스 [2]히스토리 프레임워크는 BackbaseDojo 같은 보다 큰 라이브러리안에 통합된 형태로 되어 있다. 이들 프레임워크는 AJAX 응용 프로그램에 대해서 완전히 다른 프로그래밍 모델을 제공하고 있으며, 개발자가 히스토리 기능을 사용하기 위해서는 완전히 새로운 접근방법을 사용할 것을 강요받게 된다.

([2]역주: 히스토리(History)는 "기록" 또는 "방문기록"으로 옮기지만 대부분이 히스토리에 익숙하기 때문에 번역하지 않았다)

반면에, RSH는 기존 AJAX 시스템에 포함시킬 수 있는 간단한 모듈로 되어 있다. 뿐만 아니라, RSH 라이브러리는 다른 히스토리 프레임워크들이 갖고 있는 버그들을 피하기 위한 기술들을 사용하고 있다.

RSH 히스토리 프레임워크는 자바스크립트 클래스 DhtmlHistory, HistoryStorage로 구성되어있다.

DhtmlHistory 클래스는 에이잭스 응용 프로그램을 위한 히스토리 추상화를 제공한다. 즉, 에이잭스 페이지는 브라우저에 새 위치와 그에 대한 히스토리 데이터를 지정한 히스토리 이벤트를 추가하는 add()를 호출한다. DhtmlHistory 클래스는 A 태그에 #new-location과 같은 해쉬를 사용해서 브라우저의 현재 URL을 업데이트하고, 히스토리 데이터와 새 URL을 연결한다. 에이잭스 응용 프로그램 자체를 히스토리 리스너(listener)로 등록하고, 사용자가 뒤로 이동, 앞으로 이동 버튼을 사용해서 이동할 때 마다 히스토리 이벤트는 add() 호출로 저장했던 히스토리 데이터와 브라우저의 위치를 제공한다.

개발자는 HistoryStorage 클래스를 사용해서 어떤 크기의 히스토리 데이터라도 저장할 수 있다. 일반 페이지에서 사용자가 새로운 웹 사이트로 이동할 때 브라우저는 이전 웹 페이지의 모든 응용 프로그램을 제거하고, 응용 프로그램과 자바스크립트의 상태를 정리한다. 따라서, 사용자가 뒤로 이동 버튼을 사용하여 이전 페이지로 돌아가면 모든 데이터가 사라진다. HistoryStorage 클래스는 put(), get(), hasKey()와 같은 간단한 해쉬 테이블 메서드를 제공하는 API를 통해서 이 문제를 해결한다. 사용자가 웹 페이지를 떠난 다음에도 개발자는 이들 메서드를 사용해서 데이터를 저장할 수 있다. 사용자가 뒤로 이동 버튼을 사용해서 돌아오면 HistoryStorage 클래스를 사용해서 데이터에 접근할 수 있다. 이 기능은 사용자가 웹 페이지를 떠난 후에도 브라우저가 폼 양식에 있는 값들을 자동으로 저장한다는 사실을 이용한 것으로, 숨겨진 폼 필드를 사용해서 구현됐다.

예제

이제 바로 간단한 예제로 살펴보자.

먼저, RSH 프레임워크를 사용할 페이지는 dhtmlHistory.js 스크립트를 반드시 인클루드해야 한다.

<!-- Load the Really Simple
       History framework -->
<script type="text/javascript"
            src="../../framework/dhtmlHistory.js">
</script>


DHTML 히스토리 응용 프로그램은 에이잭스 웹 페이지가 있는 디렉터리에 blank.html 파일을 포함시켜야 한다. 이 파일은 RSH 프레임워크에 함께 들어있으며, 인터넷 익스플로러에서 사용한다. 여담이지만, 인터넷 익스플로러는 히스토리 변경 사항을 추적하고 추가하기 위해 "숨겨진 iframe"을 사용한다. 히스토리 기능이 제대로 동작하기 위해서는 실제 위치를 가리키는 것이 필요한데, 이를 위해 비어있는 iframe을 사용한다. 그래서 이름도 blank.html이다.

RSH 프레임워크는 브라우저 히스토리를 조작하기 위한 진입점으로 dhtmlHistory라는 전역 객체를 생성한다. dhtmlHistory로 작업하는 첫번째 단계는 페이지 로딩이 끝난 후에 이 객체를 초기화하는 것이다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();

다음으로, 개발자는 히스토리 변경 이벤트를 [3]구 독하기 위해 dhtmlHistory.addListener()를 사용해야 한다. 이 메서드는 DHTML 히스토리가 변경될 때, 페이지의 새로운 위치, 히스토리 변경 이벤트와 관련된 히스토리 데이터를 인자로 받는 콜백 함수를 인자로 받는다.

([3]역주: 이벤트 구독보다는 이벤트 감시가 더 적절한 표현이지만 원문이 subscribe이므로 구독으로 옮김)

window.onload = initialize;
     
function initialize() {
// DHTML 히스토리 프레임워크를 초기화한다
   dhtmlHistory.initialize();
   
// HTML 히스토리 변경 이벤트를 구독한다
   dhtmlHistory.addListener(historyChange);

historyChange() 메서드는 사용자가 새 위치로 이동한 후의 newLocation과 히스토리 변경 이벤트와 관계된 historyData를 인자로 받는 함수로 이해하기 쉽다.

/** 히스토리 변경 이벤트를 받기 위한 콜백 */
function historyChange(newLocation,
                                  historyData) {
   debug("A history change has occurred: "
            + "newLocation="+newLocation
            + ", historyData="+historyData,
            true);
}


위에서 사용한 debug() 메서드는 예제 소스 파일에 정의된 유틸리티 함수로 전체 예제 안에 포함되어 있다. debug()는 웹 페이지안에 메시지를 출력한다. 두번째 인자는 불리언(boolean) 타입으로 위 인자에서는 true를 사용했다. true를 사용하면 앞의 메시지를 모두 정리한 다음에 새로운 디버그 메시지를 출력한다.

개 발자는 add() 메서드를 사용해서 히스토리 이벤트를 추가한다. 히스토리 이벤트를 추가하는 것은 edit:SomePage와 같이 히스토리 변경 이벤트와 함께 저장할 historyData 값을 제공하기 위한 새 위치를 지정하는 것이다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);

add() 를 호출하는 즉시 사용자 브라우저의 URL 툴바에 새로운 위치가 표시된다. 예를 들어, http://codinginparadise.org/my_ajax_app 주소의 에이잭스 웹 페이지에서 dhtmlHistory.add( "helloworld", "Hello World Data" )를 호출하면 사용자는 브라우저의 URL 툴바에서 다음 주소를 보게 된다.

http://codinginparadise.org/my_ajax_app#helloworld

사 용자는 이 페이지를 북마크할 수 있다. 사용자가 이 북마크를 이용하면 에이잭스 응용 프로그램은 #helloworld 값을 읽어서 웹 페이지를 초기화할 수 있다. 해시 # 다음의 위치 정보는 RSH 프레임워크에 의해 투명하게 인코딩과 디코딩이 수행된다.

historyData 는 URL을 사용하는 것 보다 쉽게 에이잭스 위치 변화에 따른 복잡한 상태들을 저장할 수 있게 해준다. historyData는 숫자, 문자열, 객체와 같은 자바스크립트 타입으로 사용할 수 있는 값이다. 이를 사용하는 예로는 사용자가 DHTML 텍스트 에디터에서 다른 페이지로 이동하는 경우에 에디터의 내용을 모두 저장하는 것이다. 사용자가 뒤로 이동 버튼을 클릭해서 다시 DHTML 페이지로 돌아오면 브라우저는 히스토리 변경 리스너에게 객체를 돌려주게 된다.

개발자는 중첩된 객체나 복잡한 상태를 표현하는 배열과 같은 자바스크립트 객체 전체를 historyData에 사용할 수 있다. 히스토리 데이터에는 간단한 데이터 타입이나 NULL 타입 뿐만 아니라 JSON(자바스크립트 객체 표기법)으 로 표현할 수 있는 무엇이든 될 수 있다. 그러나, DOM 객체에 대한 참조나 XMLHttpRequest와 같은 스크립트 가능한 브라우저 객체는 저장되지 않는다. historyData는 북마크와 함께 보존되는 데이터가 아니며, 브라우저를 종료하거나, 브라우저 캐시를 제거하거나, 사용자가 히스토리를 정리하면 사라지는 데이터이다.

dhtmlHistory를 사용하는 마지막 단계는 isFirstLoad() 메서드이다. 어떤 브라우저에서는 웹 페이지를 항해할 때 다른 페이지로 이동했다가 뒤로 이동 버튼을 눌러서 처음 사이트로 돌아오면 첫번째 페이지가 완전히 재로딩되고 onload 이벤트가 발생한다. 페이지를 반복적으로 재로딩하는 경우가 아니라 처음 로드될 때만 특별한 방법으로 초기화하길 원하는 코드를 망쳐버릴 수 있다. isFirstLoad() 메서드는 웹 페이지를 처음 로드한 경우와 사용자가 브라우저 히스토리에 저장된 웹 페이지에서 뒤로 이동한 경우를 구별해준다.

예제 코드에서는 페이지가 처음 로드되었을 때 히스토리 이벤트를 추가하길 원한다. 페이지가 로드된 다음에 사용자가 페이지로 돌아가기 위해 뒤로 이동 버튼을 누른 경우에는 히스토리 이벤트를 추가하지 않는다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);

이제 historyStorage 클래스를 살펴보자. dhtmlHistory, historyStorage와 마찬가지로 historyStorage라는 전역 객체 하나를 통해서 모든 기능을 제공한다. 이 객체는 해시 테이블을 시뮬레이션 하기 위해 put(keyName, keyValue0, get(keyName), hasKey(keyName)과 같은 메서드를 제공한다. 키 이름은 반드시 문자열이어야하며, 키 값은 XML로 된 문자열이나 자바스크립트 객체와 같이 복잡한 것도 사용할 수 있다. 예제에서는 페이지가 처음 로드될 때 historyStorage안에 간단한 XML을 저장하기 위해 put()을 사용하고 있다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);
                              
      // cache some values in the history
      // storage
      debug("Storing key 'fakeXML' into "
               + "history storage", false);
      var fakeXML =
         '<?xml version="1.0" '
         +         'encoding="ISO-8859-1"?>'
         +         '<foobar>'
         +             '<foo-entry/>'
         +         '</foobar>';
      historyStorage.put("fakeXML", fakeXML);
   }

다음으로, 사용자가 다른 페이지로 이동한 다음에 뒤로 이동 버튼을 통해 돌아온 경우에 get() 메서드를 사용해서 저장된 값을 가져올 수 있고, hasKey()를 사용해서 키가 있는지 확인할 수 있다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);
                              
      // cache some values in the history
      // storage
      debug("Storing key 'fakeXML' into "
               + "history storage", false);
      var fakeXML =
         '<?xml version="1.0" '
         +         'encoding="ISO-8859-1"?>'
         +         '<foobar>'
         +             '<foo-entry/>'
         +         '</foobar>';
      historyStorage.put("fakeXML", fakeXML);
   }
   
   // retrieve our values from the history
   // storage
   var savedXML =
                     historyStorage.get("fakeXML");
   savedXML = prettyPrintXml(savedXML);
   var hasKey =
                historyStorage.hasKey("fakeXML");
   var message =
      "historyStorage.hasKey('fakeXML')="
      + hasKey + "<br>"
      + "historyStorage.get('fakeXML')=<br>"
      + savedXML;
   debug(message, false);
}

preetyPrintXml()은 전체 예제에 포함된 유틸리티 메서드로 디버깅을 위해 웹 페이지에 XML을 출력한다.

데 이터는 오직 현재 페이지의 히스토리에서만 유지된다는 점에 주의해야 한다. 즉, 브라우저를 종료하거나 사용자가 새로운 창을 열고 에이잭스 응용프로그램의 주소를 다시 입력하는 경우에 히스토리 데이터는 이용할 수 없다. 히스토리 데이터는 오직 뒤로, 앞으로 이동 버튼에 한해서만 유지된다. 사용자가 브라우저를 종료하거나 캐시를 정리하면 사라진다. 실제로, 오랜시간 유지할 수 있는 방법을 원한다면 에이잭스 대용량 저장 시스템(AMAXX, Ajax Massive Storage System)을 보기 바란다.

예제는 이것으로 끝났으며, 데모를 체험해보거나 전체 소스 코드를 다운로드하기 바란다.

예제2: 오라일리 메일

두 번째 예제는 Gmail과 유사한 오라일리 메일이라는 가짜 AJAX 이메일 응용프로그램이다. 오라일리 메일을 통해 dhtmlHistory 클래스를 사용해서 브라우저 히스토리를 제어하는 방법, historyStorage 객체를 사용해서 히스토리 데이터를 캐시하는 방법에 대해 설명할 것이다.

오라일리 메일 사용자 인터페이스는 두 부분으로 구성되어 있다. 페이지의 왼쪽 사이즈는 받은 편지(Inbox), 임시 보관(Draft) 같은 다양한 이메일 폴더와 옵션으로 구성된 메뉴다. 사용자가 받은 편지 같은 메뉴를 선택하면 페이지의 오른쪽에 내용을 업데이트한다. 실제 응용프로그램에서는 선택된 메일박스의 내용을 가져와서 표시해야하지만 오라일리 메일에서는 선택한 옵션만 간단하게 보여준다.

오라일리 메일은 브라우저 히스토리에 메뉴 변경사항을 추가하고, 위치 바를 업데이트하는데 RSH 프레임워크를 사용하기 때문에 사용자가 응용프로그램을 북마크하고 브라우저의 뒤로, 앞으로 이동 버튼을 사용해서 메뉴 변경 이전으로 이동할 수 있다.

historyStorage 사용법을 설명하기 위해 Address Book(주소록)이라는 메뉴를 추가했다. 주소록은 이름과 이메일 주소를 자바스크립트 배열로 저장하고 있지만 실제 응용프로그램이라면 원격 서버에서 배열 내용을 가져와야 할 것이다. 그러나, 오라일리 메일에서는 배열을 직접 생성하고, 이름과 이메일 주소를 몇 개 추가한 다음에 historyStorage 객체에 저장한다. 사용자가 웹 페이지를 떠난후에 돌아오면 오라일리 메일 응용프로그램은 원격 서버에서 접속하는 대신 캐시에서 주소록을 가져온다.

주소록은 initialize() 메서드에서 저장하고 가져올 수 있다.

/** 페이지가 로딩을 끝낸 후에 초기화를 수행하는 함수 */
function initialize() {
    // DHTML 히스토리 프레임워크를 초기화한다
    dhtmlHistory.initialize();
   
    // DHTML 히스토리 리스너에 등록한다
    dhtmlHistory.addListener(handleHistoryChange);

    // 주소록을 가져올 수 없으면 직접 추가한 주소록을
    // 히스토리 저장소에서 캐싱한다.
   if (window.addressBook == undefined) {
         // 주소록을 전역 객체로 저장한다
       // 실제 응용프로그램에서는 백그라운드에서 서버로부터
       // 주소록을 가져와야한다
         window.addressBook =
             ["Brad Neuberg 'bkn3@columbia.edu'",
               "John Doe 'johndoe@example.com'",
               "Deanna Neuberg 'mom@mom.com'"];
               
         // 주소록이 있으면 이를 캐시에 보관한다
       // 사용자가 페이지를 떠난 후에 뒤로 이동으로 돌아온 경우에도 사용한다
       historyStorage.put("addressBook",
                                     addressBook);
    }
    else {
         // 히스토리 저장소에서 캐시된 주소록을 가져온다
         window.addressBook =
                      historyStorage.get("addressBook");
    }

히 스토리 변경을 다루는 코드는 이해하기 쉽다. 아래 코드에서처럼 사용자가 뒤로 또는 앞으로 이동 버튼을 클릭할 때 handleHistoryChange가 호출된다. 그러면 newLocation을 갖게 된다. 이를 이용해서 사용자 인터페이스를 올바른 상태로 업데이트할 수 있다.

/** 히스토리 변경 이벤트를 처리한다 */
function handleHistoryChange(newLocation,
                                           historyData) {
    // 위치가 없으면 수신함의 기본 위치를 보여준다
    if (newLocation == "") {
         newLocation = "section:inbox";
    }
   
    // 위치 변화가 있으면 표시할 섹션을 추출한다.
    // newLocation은 "section:"으로 시작한다
    newLocation =
             newLocation.replace(/section\:/, "");
   
    // DHTML 히스토리 변경에 따라 브라우저를 업데이트한다
    displayLocation(newLocation, historyData);
}

/** 오른쪽 컨텐트 영역에 주어진 위치를 표시한다*/
function displayLocation(newLocation,
                                     sectionData) {
    // 선택한 메뉴 항목을 가져온다
    var selectedElement =
                  document.getElementById(newLocation);
                 
    // 이전에 선택된 메뉴 항목을 제거한다
    var menu = document.getElementById("menu");
    for (var i = 0; i < menu.childNodes.length;
                                                               i++) {
         var currentElement = menu.childNodes[i];
         // DOM 요소 노드인지 확인한다
         if (currentElement.nodeType == 1) {
             //모든 클래스 이름을 제거한다
             currentElement.className = "";
         }                                                         
    }
   
    // UI에서 다르게 나타나는 새로 선택된 메뉴 항목
    selectedElement.className = "selected";
   
    // 화면 오른쪽에 새로운 섹션을 표시한다.
    // sectionData에 따라 섹션을 결정한다

    // 앞서 캐시된 로컬 주소 데이터를 사용해서 주소록을 보여준다
    if (newLocation == "addressbook") {
         // 주소록을 보여준다
         sectionData = "<p>Your addressbook:</p>";
         sectionData += "<ul>";
         
         // 주소록이 아직 없다면 캐시에서 주소록을 가져온다
         if (window.addressBook == undefined) {
             window.addressBook =
                      historyStorage.get("addressBook");
         }
         
         // 주소록을 표시한다
         for (var i = 0;
                      i < window.addressBook.length;
                               i++) {
             sectionData += "<li>"
                                    + window.addressBook[i]
                                    + "</li>";                          
         }
         
         sectionData += "</ul>";
    }
   
    // sectionData가 없다면 원격으로 가져온다. 이 예제에서는 주소록을 제외한
    // 모든 데이터는 가짜를 이용하고 있다
    if (sectionData == null) {
         // 실제 응용프로그램에서는 섹션의 내용을 원격으로 가져와야한다
         sectionData = "<p>This is section: "
             + selectedElement.innerHTML + "</p>";  
    }
   
    // 제목과 내용을 업데이트한다
    var contentTitle =
             document.getElementById("content-title");
    var contentValue =
             document.getElementById("content-value");
    contentTitle.innerHTML =
                                    selectedElement.innerHTML;
    contentValue.innerHTML = sectionData;
}


오라일리 메일 데모를 체험하거나 소스 코드를 다운로드할 수 있다.

결론

RSH(Really Simple History) API를 사용해서 에이잭스 응용프로그램에서 즐겨찾기와 뒤로, 앞으로 이동 버튼을 사용하기 위한 방법을 보았으며 직접 에이잭스 응용프로그램을 작성하는데 발판이 될 수 있는 예제 코드들을 살펴보았다. 나는 즐겨찾기와 히스토리를 완전히 지원하는 여러분의 에이잭스 발명품들을 볼 수 있기를 기대한다.

감사의 글

이 글을 검토해준 모든 사람과 RSH 프레임워크를 제작한 모두에게 감사드린다: Michael Eakes, Jeremy Sevareid, David Barrett, Brendon Wilson, Dylan Parker, Erik Arvidsson, Alex Russell, Adam Fisk, Alex Lynch, Joseph Hoang Do, Richard MacManus, Garret Wilson, Ray Baxter, Chris Messina, and David Weekly.

참고자료
  • 이 기사의 모든 소스코드 다운로드
  • RSH 프레임워크 다운로드
  • 오라일리 메일 데모와 오라일리 메일 소스 코드 다운로드. 전체 예제 다운로드에는 시험할 수 있는 보다 많은 예제들이 있다
  • Coding in Paradise(코딩천국): AJAX, DHTML, 자바 기술, 위키위키와 같은 협업 기술 등을 다루는 저자의 웹로그
  • "Coding in Paradise: AJAX: Bookmarks and Back Buttons, Advanced Example"
  • "Coding in Paradise: Safari: No DHTML History Possible"
  • "Coding in Paradise: AJAX Tutorial: Saving Session Across Page Loads Without Cookies, On The Client Side"
  • "Coding in Paradise: AJAX History Libraries"
Bard Neuberg는 모질라, JXTA, 자카르타 피드 파서 등에 코드를 공헌하고 있으며, 오픈소스 커뮤니티에서 폭넓은 공헌을 하고 있다.


출처 :
http://network.hanbitbook.co.kr/



'Scripter > AJAX' 카테고리의 다른 글

Ajax Framework 분석  (0) 2009.05.04
CSSHttpRequest  (0) 2008.11.12
HTTP Header에 대하여  (0) 2008.11.08
100라인 Ajax Wrapper  (0) 2008.04.19
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |

100라인 Ajax Wrapper

Scripter/AJAX / 2008. 4. 19. 15:13



다른 분들은 어떨지 모르나 UI 개발자인 데꾸벅의 경우는 프로젝트를 할때 마다 느끼는 거지만 어떤 Ajax framework를 쓸지 고민을 많이 하는 편이다.
그냥 규모가 좀 되는곳이라면 jQuery나 prototype을.. 그리고 내부 애플리케이션이라면 extjs를... 혹은 작더라도 struts2를 쓸때는 dojo를...
근데 문제가 되는것은 아주 작은 소규모 프로젝트의 경우는 이러한 ajax framework나 libraries를 사용하기가 좀 그래서 단순 XHR Wrapper를 만들어 사용한다



[데꾸벅이 사용하는 Ajax Wrapper 01]

function createHttp() {
 try {
  return new XMLHttpRequest();
 }
 catch (e) {
  var objectNames = ["MSXML2.XMLHTTP.5.0",
"MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0",
"MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
  for (var i = 0; i < objectNames.length; i ++) {
   try {
    return new ActiveXObject(objectNames[i]);
    break;
   }
   catch (e) {
   }
  }
  return null;
 }
}


var loading = false;
function getResponse(uri,content) {
 try {
  loading = true
  var body = document.body;
  var oHttp = createHttp();
  if(uri.indexOf('?') ==-1) aux = '?';
  else aux = '&';
  oHttp.open("POST", uri + aux+"time=" + (new Date()).getTime(), false);
  if(content == undefined) {
   content = '';
  } else {
   oHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  }
  oHttp.send(content);
  result = new Array();
  if(isSafari || isOpera) {
    resultNodes = oHttp.responseXML.firstChild.childNodes;
    for(var i=0; i';

  return false;
 }
}

function requestHttp(uri) {
 try{
  var oHttp = createHttp();
  oHttp.open("GET", uri + "&time=" + (new Date()).getTime(), false);
  oHttp.send("");
  if(isSafari || isOpera) {
   var returnValue = oHttp.responseXML.firstChild.firstChild.nextSibling.firstChild.nodeValue;
   delete oHttp;
   return returnValue;
  } else {
   var returnValue = oHttp.responseXML.selectSingleNode("/response/error").text;
   delete oHttp;
   return returnValue;
  }
 } catch (e) {
  window.status = e.messge;
 }
}

function requestHttpText(uri) {
 var oHttp = createHttp();
 oHttp.open("POST", uri + "&time=" + (new Date()).getTime(), false);
 oHttp.send("");
 var returnValue = oHttp.responseText;
 delete oHttp;
 return returnValue;
}

function requestHttpXml(uri) {
 var oHttp = createHttp();
 oHttp.open("GET", uri + "&time=" + (new Date()).getTime(), false);
 oHttp.send("");
 var returnValue = oHttp.responseXML;
 delete oHttp;
 return returnValue;
}

function requestPost(uri, content) {
 var oHttp = createHttp();
 oHttp.open("POST", uri + "&time=" + (new Date()).getTime(), false);
 oHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
 oHttp.send(content+ "&time=" + (new Date()).getTime());
 var returnValue = oHttp.responseXML.selectSingleNode("/response/error").text;
 delete oHttp;
 return returnValue;
}

function requestPostText(uri, content) {
 var oHttp = createHttp();
 oHttp.open("POST", uri + "&time=" + (new Date()).getTime(), false);
 oHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
 oHttp.send(content);
 var returnValue = oHttp.responseText;
 delete oHttp;
 return returnValue;
}


function setRequestBody(elementName, elementValue, boundary){
 var body = "";
 body += "--" + boundary + "\r\n";
 body += "Content-Disposition: form-data; name=\"" + elementName + "\"" + "\r\n\r\n";
 body += elementValue + "\r\n";
 return body;
}



[모 프로젝트에서 사용했던 예~ ]

/**
 * HTTP 혹은 HTTPS 방식의 통신을 제공하는 클래스객체를 생성합니다.
 * @class EnHttp
 *
 */
function EnHttp(){

 this.dialBack ="";    //컴포넌트와 컨테이너 인증에 사용합니다.
 this.extraInfo ="";    //Url의 추가 정보를 지정합니다.
 this.host = "127.0.0.1";  //호스트 이름을 지정합니다.
 this.method = "POST";   //메쏘드(HTTP의 Verb)를 지정합니다.
 this.optionalHeaders = "";  //HTTP요청의 추가 헤더를 지정합니다.
 this.password = "";    //패스워드를 지정합니다.
 this.port = "80";    //포트를 지정합니다.
 this.postData ="";    //POST, PUT 요청에 사용되는 데이터를 지정합니다.
 this.protocol ="HTTP";   //프로토콜을 지정합니다.
 this.proxy ="";     //프록시가 설정된 환경인 경우, 프록시설정 정보를 지정합니다.
 this.referer = "";    //HTTP요청의 Referer를 지정합니다.
 this.responseContents="";  //응답 내용 문자열을 전달합니다.
 this.responseHeaders ="";  //응답헤더 문자열을 전달합니다.
 this.responseStatusCode =""; //응답상태코드를 문자열로 전달합니다 (예: "404")
 this.responseStatusText =""; //응답상태를 문자열로 전달합니다.(예: "Not Found")
 this.ssl = false;    //SSL 사용여부를 지정합니다.
 this.timeout = 0;    //HTTP요청의 timeout을 지정합니다.
 this.urlPath="";    //경로를 지정합니다.
 this.user ="";     //사용자 이름을 지정합니다.
 this.version ="";    //HTTP버전 문자열을 지정합니다.
 this.url = "";     //전체 URL을 지정 합니다.


 /**
  * 실제 HTTP요청을 서버에 전송합니다.
  * @return true/false
  */
 this.SendRequest = function(){
  this.url = this.protocol + "://"+ this.host + ":" + this.port + this.urlPath;

  var xmlhttp = createHttp();
  xmlhttp.open(this.method, this.url,false);

  var headers = this.optionalHeaders.split("\r\n");
  for(var i=0; i < headers.length; i++){
   var oneHeaders = headers[i].split(":");
   xmlhttp.setRequestHeader(oneHeaders[0],oneHeaders[1]);
  }


  var varResponseStatusCode = "";
  var varResponseHeaders = "";
  var varResponseContents = "";

     //도착하는 이벤트 핸들러 걸어주고
     xmlhttp.onreadystatechange = function() {
         if (xmlhttp.readyState==4) {
    varResponseStatusCode = xmlhttp.status;
    varResponseHeaders = xmlhttp.getAllResponseHeaders();
             if (xmlhttp.status==200) {
     varResponseContents = xmlhttp.responseText;
    }
         }
     }
     xmlhttp.send(this.postData);
  this.responseStatusCode = varResponseStatusCode;
  this.responseHeaders = varResponseHeaders;
  this.responseContents = varResponseContents;
 }


 /**
  * 쿠키를 읽어옵니다.
  * @param {Object} url - 쿠키를 읽어올 Url주소
  * @return Cookie
  */
 this.getCookie = function(url){

 }

 /**
  * 쿠키를 설정합니다.
  * @param {Object} url- 쿠키를 설정할 Url주소.
  * @param {Object} cookie- 설정할 쿠키.
  * @return ture@false
  */
 this.setCookie = function(url,cookie){

 }


 /**
  * 파일을 form 형태로 업로드합니다.
  * @param {Object} file- 업로드할 파일명(경로).
  * @param {Object} url- 업로드할 경로.
  */
 this.upload = function(file,url){

}
}


Kris Zyp's 블로그에 Future Ajax Wrapper라는 포스트에서 cross-browser와 cross-domain을 지원하는 100라인 짜리 ajax wrapper 소스를 선보였다.

W3C에서 제안한 cross-site acess 를 사용하는 대신에 IE8의 XDR(XDomainRequest)와 새로운 cross-site request를 추가하였다고 한다. 이번에 출시된 IE8은 표준을 모두 따른다고 하더니만....   또다시 IE8용으로 분기를 해줘야 한다.. ㅡ.,ㅡ;



function doRequest(method,url,async,onLoad,onProgress) {
    var xhr;
    if ((onProgress || isXDomainUrl(url)) && window.XDomainRequest) {
         if (url.match(/^https:/) && !onProgress) {
            loadUsingScriptTag(url); 

            return;
	}
        xhr = new XDomainRequest;
	if (!url.match(/^http:/)) {
            url = absoluteUrl(location.href,url);

          }
	if (!(method == “GET” || method == “POST”)) {
	    url += “&method=” + method;
	    method = “POST”;
	}
	function xdrLoad() {
	   if (xhr.contentType.match(/\/xml/)){
		var dom = new ActiveXObject(”Microsoft.XMLDOM”);
		dom.async = false;
		dom.loadXML(xhr.responseText,200);
		onLoad(dom);
	   }
	   else {
	   	onLoad(xhr.responseText,200);
	   }
	}
	if (async === false) {
	    var loaded;
	    xhr.onload = function() {
		loaded = true;
		xdrLoad();
	    }
            xhr.open(method,url);
	    xhr.send(null);
	    while(!loaded) {
		alert(”결과를 기다리는 중입니다.”);
	    }
 	    return;
	}
	else {	// do an asynchronous request with XDomainRequest
            xhr.onload = xdrLoad;
            xhr.open(method,url);
            xhr.onprogress = onProgress;
	}
    }

    else {
        xhr = new XMLHttpRequest; 
        xhr.open(method,url,async);
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 3) 
                onProgress(xhr.responseText);
            if (xhr.readyState == 4) // finished
                onLoad(xhr.responseText,xhr.status);
        }
    }
    xhr.send(null); 


	function absoluteUrl : function(baseUrl, relativeUrl) {
		if (relativeUrl.match(/\w+:\/\//))
			return relativeUrl;
		if (relativeUrl.charAt(0)==’/') {
			baseUrl = baseUrl.match(/.*\/\/[^\/]+/)
			return (baseUrl ? baseUrl[0] : ”) + relativeUrl;
		}
		baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*$/)[0].length);
		if (relativeUrl == ‘.’)
			return baseUrl;
		while (relativeUrl.substring(0,3) == ‘../’) {
			baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*\/$/)[0].length);
			relativeUrl = relativeUrl.substring(3);
		}
		return baseUrl + relativeUrl;
	}
	function loadUsingScriptTag(url) {
		… do JSONP here if we want
	}
}

'Scripter > AJAX' 카테고리의 다른 글

Ajax Framework 분석  (0) 2009.05.04
CSSHttpRequest  (0) 2008.11.12
HTTP Header에 대하여  (0) 2008.11.08
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |
네이버블로그에 포스팅했던 글을 다시 옮기다.


어떤 AJAX Framework를 선택할 것인가.?
넥스트리내 세미나 때문에 자료를 찾다가 괜찮은 사이트를 발견했다. 번역이 매끄럽지 못하고 의역이라 왠지...

올들어 500개 이상의 Ajax Framework이 생겨났다. (2007년 기준)
프레임웍 선택은 프로젝트에 상당히 신중한 작업이다. 개발시간 및 유지보수도 프레임웍선택에 상당히 의존적이기 때문이다.

1. 서버 의존성.
서버 독립적인 프레임웍은 mashing-up 서버 기술을 가능하게 할것이며 소프트웨어 아키텍쳐를 lowly ties하게 만든다.
반면, 서버 의존적인 프레임웍은 생산력을 높이나 프로젝트를 매우 어렵게 만들게 한다.



2. 구조화된 자바스크립트 향상
만일 팀원들이 공통된 개발방법론을 공유하지 않는다면 자바스크립트는 상당히 개발하기 곤란할것이다.
생성된 DOM 객체를 접근하는 방법은 여러가지가 있다. 이것을 프레임웍이 가지고 잇는 on-demand Javascript, 패키징 능력, 향상된 OOP와 같이 정형화할 필요가 있다.
extjs의 경우는 OOP기준으로 작성된 아주 좋은 소스네..


3. 만들어진 컴포넌트의 재사용성
다음 프로젝트에서도 재사용할수 있는지 검토한다.


4.프레임워크의 현재 문서화 레벨
대부분의 프로젝트에서 주위해야 할 문제
이런점에서 본다면 extjs는 킹왕짱이네...


5. 프로젝트에서 필요로 하는것
프로젝트에서 프레임웍을 필요로 하거나 다룰수 있는가 또는 어떤것들은 GUI지향적이고, 다른기능에 중점을 두고 있고 아니면 커뮤니케이션 위주인지를 확인한다.
복잡한 폼을 다루는 금융권 사이트에서는 extjs를 쓰기가 좀 그렇다는 의미..


6. 최신버전이 얼마나 오래된 것인가?
대부분 프로젝트는 자사소유이다. 프레임웍의 발전은 프로젝트를 발전시키고 유지보수하는 능력에 달려있다.
좋은 커뮤니티 사용자는 더 기능적인것을 필요로하는 개발자와 얘기하는것 만큼 중요하다.
스폰서의 조심성과 현재 제품웹사이트에서 사용하는 프레임웍은 수년을 지속되거나 단지 6개월만에 끝나는지를 추정할수 있는 좋은 시작이다.
extjs의 경우는 다른 ajax framework에 비해 상당히 활발하고 자주 업데이트 되고 버전 호환성도 상당히 뛰어나다.

7. 어떤종류를 지원하는가?
커뮤니티 지원, 상업적 지원, 테스트하는동안 테스트반응(?)이 있는가?
jsUnit으로 테스트주도개발을 할수도 있고.. 여기서 말하는 테스트는 해당 프레임웍에 대한 지원을 말하는것 같다.



8. 프레임워크를 익히기 쉬운가 (프레임워크를 익히는 learning curve가 가파른가?)
팀이 그러한 기술력을 핸들링 할수 있는가?  많은 개발자들은 자바스크립트를 꺼려한다. 그러한 변화를 핸들링 할수 있는가?
extjs 절라 베리베리 스팁하다... 단지 데꾸벅 생각


9. 방문자들은 누구인가?
공공사이트에는 가벼운 프레임워크가 요구된다.  만일 방문자들이 대용량 자바스크립트 API를 다운로드 한다면 그들은 머지않아 사이트를 찾이 않을것이다.


원문 : 세바스찬 JUST 블로그

'Scripter > AJAX' 카테고리의 다른 글

Ajax Framework 분석  (0) 2009.05.04
CSSHttpRequest  (0) 2008.11.12
HTTP Header에 대하여  (0) 2008.11.08
AJAX에서 즐겨찾기와 뒤로가기를 다루는 방법  (0) 2008.09.24
100라인 Ajax Wrapper  (0) 2008.04.19
Post by 넥스트리소프트 데꾸벅(techbug)
, |