HTML5 PushState()를 활용한 PJAX 구현
HTML5 PushState()를 활용한 PJAX 구현
기존 AJAX 사용 시 이용할 수 없었던 브라우저 뒤로 가기(히스토리 내역을 활용한 기능)기능을 보완하기 위한 방법으로 "location.hash" 를 활용한 Hashbang(hash: # 와 bang: !의 합성)기술이 등장 했습니다.
하지만 이 방식은 뒤로 가기 기능은 처리할 수 있지만, 일종의 URL Hack 방식으로, 검색 엔진들로 하여금 인덱싱되지 않는 단점(구글은 해시뱅에 대해 인덱싱이 가능하도록 escape 처리함)을 가지고 있습니다.
또한, 방식 자체가 자바스크립트 기술(ajax)에 크게 의존하고 있기 때문에 스크립트 오류 시 깨지기 쉬운 사이트 구조를 가지게 됩니다.
이번 포스트에서 설명드릴 방식은 HTML5 명세의 PJAX(pushState + ajax)라는 기술이며, 이 기능을 통해 뒤로 가기 기능과 검색 인덱싱 처리를 동시에 보완할 수 있습니다.
하지만 HTML5 명세 기술이므로 모든 브라우저에서 지원하지 않는 단점도 가지고 있습니다.
아래는 PJAX(pushState + ajax)를 구현한(서버 + 클라이언트) 예제 코드이며, 코드에서처럼 전송 방식(ajax, normal)을 분기하여 접근 방식에 따라 사용자에게 다른 결과(컨텐츠)를 보여주게 됩니다.
- Ajax 분기 처리에 대한 더 자세한 설명은 아래 포스트 주소를 확인하시기 바랍니다.
http://mohwaproject.tistory.com/entry/Ajax-%EC%A0%84%EC%86%A1-%EA%B5%AC%EB%B6%84%ED%95%98%EA%B8%B0
public string Pjax1() { // GET 요청 시 Jquery 및 Prototype과 같은 대표적인 라이브러리에서 ajax 요청을 판단하는 요청 값(X-Requested-With)을 추가하여 요청한다. if ((Request.Headers["X-Requested-With"] != null && Request.Headers["X-Requested-With"].ToLower() == "xmlhttprequest") || Request.Headers["ORIGIN"] != null) { return "Ajax Request History 1"; } else { return "Normal Request History 1"; } } public string Pjax2() { // X-Requested-With Jquery 및 Prototype과 같은 대표적인 라이브러이에서 아래와 같은(X-Requested-With) 요청 헤더를 추가하여 요청한다. if ((Request.Headers["X-Requested-With"] != null && Request.Headers["X-Requested-With"].ToLower() == "xmlhttprequest") || Request.Headers["ORIGIN"] != null) { return "Ajax Request History 2"; } else { return "Normal Request History 2"; } }
2. 클라이언트 측(자바스크립트) 코드:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <script type="text/javascript" src="/sabo/contents/script/jquery.js"></script> <title></title> <script type="text/javascript"> /* History.pjax(History.pjax); History.push(data, title, url); */ var History = new (function () { var History = function () { return this; } History.fn = History.prototype = { pjax: function (ㅊ) { url = url || ''; callback = callback || function () { ; }; var xhr = new XMLHttpRequest(); xhr.open("get", url, true); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); var that = this; xhr.onload = function () { callback.apply(that, [xhr.response]); }; xhr.send(null); }, push: function (data, title, url) { data = data || ''; title = title || ''; url = url || ''; window.history.pushState && window.history.pushState(data, title, url); return this; } } return History; } ())(); bind(window, 'load', function () { bind(document.getElementById('pjax1'), 'click', function (e) { History.pjax('http://kin.dragonflygame.co.kr/sabo/blog/board/Pjax1/', function (data) { var data = document.getElementById('containerHTML').value + data + ", " + new Date().getTime() + ' '; // pushState 추가 this.push(data, '', window.location.href); show(data); }); window.history.pushState && stopDefault(e); }, false); bind(document.getElementById('pjax2'), 'click', function (e) { History.pjax('http://kin.dragonflygame.co.kr/sabo/blog/board/Pjax2/', function (data) { var data = document.getElementById('containerHTML').value + data + ", " + new Date().getTime() + ' '; // pushState 추가 this.push(data, '', window.location.href); show(data); }); window.history.pushState && stopDefault(e); }, false); bind(window, 'popstate', function (e) { show(e.state); }); }, false); function show(data) { if (data) document.getElementById('containerHTML').value = data; } function bind(elem, type, handler, capture) { type = typeof type === 'string' ? type : ''; handler = typeof handler === 'function' ? handler : function () { ; }; capture = capture || false; if (elem.addEventListener) { elem.addEventListener(type, handler, capture); } else if (elem.attachEvent) { elem.attachEvent('on' + type, handler); } return this; } function stopDefault(e){ if (window.event) e.returnValue = false; else if (e.preventDefault) e.preventDefault(); }; //]]> </script> </head> <body> <textarea id="containerHTML" rows="" cols="" style="width:500px;height:500px"></textarea><br /><br /> <a id="pjax1" href="http://kin.dragonflygame.co.kr/sabo/blog/board/Pjax1/">Pjax1</a> <a id="pjax2" href="http://kin.dragonflygame.co.kr/sabo/blog/board/Pjax2/">Pjax2</a><br /><br /> </body> </html>
3. 실행 결과:
PJAX 를 지원 브라우저에서는 아래 결과와 같이 모든 Ajax 요청에 대한 히스토리(뒤로/앞으로 가기 지원)를 가지게 됩니다.
반면, 그렇지 않는 브라우저(IE8-)에서는 서버측 분기 처리로 인해 <a> 요소의 href 속성에 할당된 주소의 컨텐츠를
가지게 됩니다.
일반 전송 방식에 의한 결과.
참고 사이트:
ajax 와 hashbang 그리고 pjax:
http://rkjun.wordpress.com/2012/05/29/ajax-%EC%99%80-hashbang-%EA%B7%B8%EB%A6%AC%EA%B3%A0-pjax/
HTML5 History/State API를 이용한 Ajax 히스토리 구현:
http://firejune.com/1743#0