'HTML5'에 해당되는 글 18건

  1. 2012.08.03 HTML5 File API 구현
  2. 2012.07.26 HTML5 Full Screen API 간단 구현
  3. 2012.07.18 HTML5 PushState()를 활용한 PJAX 구현 1
  4. 2012.07.17 Web Audio API(Webkit) 기본 사용법 1
  5. 2012.07.06 SqlLite Helper
  6. 2012.05.23 HTML5 API - APPCACHE
  7. 2012.05.22 HTML5 API - LocalStorage, SessionStorage
  8. 2012.05.21 HTML5 API - Web Worker
  9. 2012.05.18 HTML5 API - FORM
  10. 2012.05.17 웹에서 실시간 데이터 처리하기(WebSocket, Comet) 3

HTML5 File API 구현




HTML5 File API 구현



웹 브라우저(Gecko, Webkit 등..)상에서 로컬 영역의 특정 파일을 접근, 조작할 수 있는 여러가지 방법을 제공합니다.


단 제공되는 File Interface는 로컬 보안 상 파일 탐색기 또는 Drag & Drop 방식을 이용해 사용자가 직접 선택한 File로 한정 시킵니다.




아래는 관련 소스를 모듈화 시킨 코드이며, 각 기능은 아래와 같습니다.



1. 생성자 함수:

File(form || undefined)

form: 해당 File 컨트롤을 포함하는 부모 엘리먼트(보통 Form 엘리먼트가 해당됨)




2. 각 File 컨트롤의 onchange 이벤트 등록

.change(callback);

callback: onchange 이벤트 시 핸들러




3. 각 Drop Zone 엘리먼트의 dragenter 이벤트 등록

.dragenter(elems, callback)

elems: 엘리먼트 or 엘리먼트 배열

callback: ondragenter 이벤트 시 핸들러




4. 각 Drop Zone 엘리먼트의 dragleave 이벤트 등록

dragleave(elems, callback)

elems: 엘리먼트 or 엘리먼트 배열

callback: dragleave 이벤트 시 핸들러




5. 각 Drop Zone 엘리먼트의 dragover 이벤트 등록

dragover(elems, callback)

elems: 엘리먼트 or 엘리먼트 배열

callback: dragover 이벤트 시 핸들러




6. 각 Drop Zone 엘리먼트의 dragdrop 이벤트 등록

dragdrop(elems, callback)

elems: 엘리먼트 or 엘리먼트 배열

callback: dragover 이벤트 시 핸들러




7. progress 관련 이벤트 등록

progress(data, before, update, load, dataType)

data: file 객체

before: 읽어드린 file 객체의 onloadstart 이벤트 시 핸들러

update: 읽어드린 file 객체의 onprogress 이벤트 시 핸들러

load: 읽어드린 file 객체의 onload 이벤트 시 핸들러

dataType: file 객체의 저장 데이터 타입




8. file 객체 반복자

File.each(files, callback, type)

files:컨트롤을 통해 선택된 파일 리스트

callback: 각 컨트롤의 반복자 callback

type: 허용 file 타입




9. 파일 읽기

Fil.read(data, callback, dataType)

data: 읽어드릴 file 객체

callback: 읽어드린 file 객체의 onload 이벤트 시 핸들러

dataType: file 객체의 저장 데이터 타입




10. Blob 생성

File.readBlob(data, start, end, callback)

data: 읽어드릴 file 객체

start: 시작 바이트 배열 index

end: 마지막 바이트 배열 index

callback: 읽어드린 file 객체의 onload 이벤트 시 핸들러





- 관련된 자세한 소스는 아래 jsbin.com에 등록된 페이지를 살펴 보시기 바랍니다.






1. JSBin 소스 링크:

http://jsbin.com/urupic/2




2. 모듈 사용방법:

  window.onload = function (e) {
        //var target = document.getElementById('spin');
        //var spinner = new Spinner(opts).spin(target);

        var zone1 = document.getElementById('drop_zone1');
        var zone2 = document.getElementById('drop_zone2');
        var zone3 = document.getElementById('drop_zone3');

        var zones = [zone1];

        var progress = document.getElementById('progress_bar');
        var progress_percent = document.getElementById('progress_percent');

        File().change(function (e, files) {

            zone1.innerHTML = "Loading...";

            var that = this;
            var h = [];

            File.each(files, function (idx) {

                File.read(this, function (e, result) {

                    var html = '<li>';
                    html += '<div style="margin-top:20px;margin-left:10px;">';
                    html += '<img src="' + result + '" width="200" height="200" style="border:gray 2px solid" />';
                    html += '<div style="margin-top:10px"> name: ' + escape(this.name) + ' type: ' + this.type + ' size: ' + this.size + ', lastModifiedDate: ' + this.lastModifiedDate + ',' + this.lastModifiedDate.toLocaleDateString() + '</div>';
                    html += '<textarea style="margin-top:10px;width:600px;height:200px">' + result + '</textarea>';
                    html += '</div>';
                    html += '</li>';

                    h.push(html);

                    if (idx >= files.length - 1) document.getElementById('image_list').innerHTML += h.join('');

                }, 'dataUrl');

                /*
                File.readBlob(this, 0, this.size, function (e, result) {
                });
                */

                that.progress(this, function (e) {
                    progress_percent.style.width = '0%';
                    progress_percent.textContent = '0%';

                    progress.className = 'loading';
                },
			    function (e, loaded, total) {

			        var percent = Math.round((loaded / total) * 100);

			        if (percent < 100) {
			            progress_percent.style.width = percent + '%';
			            progress_percent.textContent = percent + '%';
			        }
			    },
			    function (e) {
			        progress_percent.style.width = '100%';
			        progress_percent.textContent = '100%';

			        if (idx >= files.length - 1) {
			            zone1.innerHTML = "Upload Complete";
			            window.setTimeout(function () { progress.className = ''; }, 2000);
			        }
			    });

            }, 'image');
        })
	    .dragenter(zones, function (e, zone) {
	        zone.style.backgroundColor = 'orange';
	        zone.style.color = 'black';
	    })
	    .dragover(zones, function (e, zone) {
	        zone.style.backgroundColor = 'orange';
	        zone.style.color = 'black';
	    })
	    .dragleave(zones, function (e, zone) {
	        zone.style.backgroundColor = 'white';
	        zone.style.color = '#BBB';
	    })
	    .dragdrop(zones, function (e, files, zone) {

	        zone.innerHTML = "Loading...";

	        var that = this;
	        var h = [];

	        File.each(files, function (idx) {

	            File.read(this, function (e, result) {

	                var html = '<li>';
	                html += '<div style="margin-top:20px;margin-left:10px;">';
	                html += '<img src="' + result + '" width="200" height="200" style="border:gray 2px solid" />';
	                html += '<div style="margin-top:10px"> name: ' + escape(this.name) + ' type: ' + this.type + ' size: ' + this.size + ', lastModifiedDate: ' + this.lastModifiedDate + ',' + this.lastModifiedDate.toLocaleDateString() + '</div>';
	                html += '<textarea style="margin-top:10px;width:600px;height:200px">' + result + '</textarea>';
	                html += '</div>';
	                html += '</li>';

	                h.push(html);

	                if (idx >= files.length - 1) document.getElementById('image_list').innerHTML += h.join('');

	            }, 'dataUrl');

	            /*
	            File.readBlob(this, 0, this.size, function (e, result) {
	            });
	            */

	            that.progress(this, function (e) {
	                progress_percent.style.width = '0%';
	                progress_percent.textContent = '0%';

	                progress.className = 'loading';
	            },
			    function (e, loaded, total) {

			        var percent = Math.round((loaded / total) * 100);

			        if (percent < 100) {
			            progress_percent.style.width = percent + '%';
			            progress_percent.textContent = percent + '%';
			        }
			    },
			    function (e, result) {
			        progress_percent.style.width = '100%';
			        progress_percent.textContent = '100%';

			        if (idx >= files.length - 1) {
			            zone.style.backgroundColor = 'white';
			            zone.style.color = '#BBB';
			            zone.innerHTML = "Upload Complete";

			            window.setTimeout(function () { progress.className = ''; }, 2000);
			        }
			    });

	        }, 'image');
	    });
    }


    function bind(elem, type, handler, capture) {

        type = typeof type === 'string' && type || '';
        handler = handler || function () { ; };

        if (elem.addEventListener) {
            elem.addEventListener(type, handler, capture);
        }
        else if (elem.attachEvent) {
            elem.attachEvent('on' + type, handler);
        }

        return elem;
    };





3. 실행화면 및 브라우저 지원 상황:







- 브라우저 지원 상황:



http://caniuse.com/#feat=fileapi






5. 참고 사이트:


[HTML5강좌] 20. File API:

http://sqler.pe.kr/OSS/bIISLec/402253


Html5rocks File API:

http://www.html5rocks.com/en/tutorials/file/dndfiles/


W3c File API:

http://www.w3.org/TR/FileAPI/#dfn-Blob





HTML5 Full Screen API 간단 구현



HTML5 Full Screen API 간단 구현



웹 브라우저(Gecko, Webkit 등..)상에서 특정 엘리먼트 영역을 전체 화면(툴바 영역이 보이지 않는)으로 변경할 수 있는 FullScreen API가 있습니다.



API에 대한 설명은 그리 어렵지 않아 따로 설명 드리지 않도록 하겠습니다.




아래는 관련 소스를 모듈화 시킨 코드 입니다.




1. JSBin 소스 링크:

http://jsbin.com/ayicib/3




2.모듈 사용방법:

window.onload = function () {

        var contextStart = document.getElementById('specialstuffStart');

        var btnStart = document.getElementById('fsbuttonStart');
        var btnEnd = document.getElementById('fsbuttonEnd');

        bind(btnStart, 'click', function () {
            FullScreen.on(contextStart, function (e) {
                btnStart.style.display = 'none';
                btnEnd.style.display = 'block';
            }, function (e) {
                btnStart.style.display = 'block';
                btnEnd.style.display = 'none';
            });
        }, false);

        bind(document, 'keyup', function (e) {
            // space key
            if (e.keyCode === 32) FullScreen.off();
        }, false);
    }

    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;
    }



FullScreen.on(contextStart, function (e) {
                btnStart.style.display = 'none';
                btnEnd.style.display = 'block';
            }, function (e) {
                btnStart.style.display = 'block';
                btnEnd.style.display = 'none';
            });


FullScreen.on(context, open, close);


context: 전체화면 영역

open: 전체화면 open 시 이벤트 핸들러

close: 전체화면  close 시 이벤트 핸들러



        bind(document, 'keyup', function (e) {
            // space key
            if (e.keyCode === 32) FullScreen.off();
        }, false);


FullScreen.off(): 전체화면 중지.




3. 실행화면 및 브라우저 지원 상황:



 실행화면:






브라우저 지원 상황:






참고 사이트:

http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/





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






1. 서버측(.NET MVC 기준) 코드:

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





Web Audio API(Webkit) 기본 사용법



Web Audio API(Webkit) 기본 사용법



지난 7월 7일 NHN 개발자 블로그인 "Hello World"에서 열린 "제2회 Front-End 개발자들을 위한 오픈 세미나" 가 있었습니다.

   

참석은 하였지만, 개인적인 사정으로 두 번째 섹션까지밖에 들을 수 없어 많이 아쉬웠던 세미나 이기도 합니다.;;ㅠㅠ

    

아래 작성된 코드는 이날 세미나의 세 번째 섹션인 "Web Audio API"에 대한 소스를 이리저리 찾아보며(구글링을 통해) 작성된 예제 코드입니다.


또한, 코드 작성 시 기술(음향)에 대한 전문적인 지식이 많이 부족하여 소스에 대한 응용이 전무합니다.;;;(API에 대한 한글 문서가 없다는 점과 기반 지식이 부족하여 몇몇 소스는 이해하기도 어려었습니다.;;)





1. 사운드 데이타 요청(Ajax) 시 응답 HTTP Header는 아래와 같습니다.










2. 작성된 소스는 아래와 같으며, 기본적인 API 사용에 관해 관심 있으신 분은 아래 소스를 테스트해보시기 바랍니다.


  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head id="Head1" runat="server">
  4. <script type="text/javascript" src="/sabo/contents/script/jquery.js"></script>
  5. <title></title>
  6. <script type="text/javascript">
  7. //<![CDATA[
  8.        
  9.     // BufferLoader
  10.     // Ajax 방식을 통해 사운드 데이터를 바이너리로 저장한다.
  11.     var BufferLoader = (function () {
  12.  
  13.         var BufferLoader = function (opt) {
  14.  
  15.             return new BufferLoader.fn.init(opt);
  16.         }
  17.  
  18.         BufferLoader.fn = BufferLoader.prototype = {
  19.             init: function (opt) {
  20.  
  21.                 this.ctx = new webkitAudioContext();
  22.                 this.urls = [];
  23.                 this.buffers = [];
  24.                 this.callback = function () { ; };
  25.                 this.errcallback = function () { ; };
  26.                 this.loaderCount = 0;
  27.  
  28.                 marge.call(this, opt);
  29.  
  30.                 return this;
  31.  
  32.             },
  33.             loader: function () {
  34.                 for (var i = 0, length = this.urls.length; i < length; i++) {
  35.                     load.apply(this, [this.urls[i], this.callback, this.errcallback]);
  36.                 }
  37.             }
  38.         }
  39.  
  40.         function load(url, callback) {
  41.  
  42.             var xhr = new XMLHttpRequest();
  43.             xhr.open("get", url, true);
  44.             // 전송받을 데이터 타입 설정 arraybuffer(바이너리)
  45.             xhr.responseType = "arraybuffer";
  46.  
  47.             var that = this;
  48.  
  49.             xhr.onload = function () {
  50.  
  51.                 that.ctx.decodeAudioData(
  52.                 xhr.response,
  53.                 function (buffer) {
  54.  
  55.                     if (!buffer) return;
  56.  
  57.                     that.buffers.push(buffer);
  58.  
  59.                     if (++that.loaderCount === that.urls.length) {
  60.                         typeof that.callback === 'function' && that.callback.call(that.ctx, that.buffers);
  61.                     }
  62.                 });
  63.  
  64.             };
  65.  
  66.             xhr.onerror = function (e) {
  67.                 if (that.loaderCount === that.urls.length) {
  68.                     typeof that.errcallback === 'function' && that.errcallback.call(that.ctx, e);
  69.                 }
  70.             };
  71.  
  72.             xhr.send();
  73.         }
  74.  
  75.         function marge() {
  76.  
  77.             var target = this
  78.                 , opts = []
  79.                 , src = null
  80.                 , copy = null;
  81.  
  82.             for (var i = 0, length = arguments.length; i < length; i++) {
  83.  
  84.                 opts = arguments[i];
  85.  
  86.                 for (var n in opts) {
  87.  
  88.                     src = target[n];
  89.                     copy = opts[n];
  90.                     target[n] = copy;
  91.                 }
  92.             }
  93.  
  94.             return target;
  95.         }
  96.  
  97.  
  98.         BufferLoader.fn.init.prototype = BufferLoader.prototype;
  99.  
  100.         return BufferLoader;
  101.  
  102.     })();
  103.  
  104.  
  105.     // WebAudio
  106.     var WebAudio = (function () {
  107.  
  108.         var that = null;
  109.  
  110.         var WebAudio = function (opt) {
  111.             return new WebAudio.fn.init(opt);
  112.         }
  113.  
  114.         WebAudio.fn = WebAudio.prototype = {
  115.             init: function (opt) {
  116.  
  117.                 this.ctx = null;
  118.                 this.buffers = [];
  119.  
  120.  
  121.                 marge.call(this, opt);
  122.  
  123.                 if (that) return that;
  124.  
  125.                 that = this;
  126.  
  127.                 return this;
  128.  
  129.             },
  130.             currentSource: null,
  131.             play: function (type, callback) {
  132.  
  133.                 type = type || '';
  134.                 callback = callback || function () { ; };
  135.  
  136.                 // 현재 플레이 되고 있는 소스 노드를 off 시키고 새로운 소스 노드를 on 시킨다.
  137.                 if (this.currentSource) this.currentSource.noteOff(0);
  138.  
  139.                 context = this.ctx;
  140.  
  141.                 this.currentSource = this.getTypeToSource(context, type);
  142.  
  143.                 var gainNode = context.createGainNode();
  144.                 gainNode.connect(context.destination);
  145.  
  146.                 this.currentSource.connect(gainNode);
  147.                 this.currentSource.noteOn(0);
  148.  
  149.                 callback.call(context);
  150.  
  151.                 return this;
  152.  
  153.             },
  154.             currentGainNode: null,
  155.             crossFadePlay: function (type, volume, callback) {
  156.  
  157.                 type = type || '';
  158.                 volume = volume || 0;
  159.                 callback = callback || function () { ; };
  160.  
  161.                 // 현재 플레이 되고 있는 소스 노드의 사운드 볼륨을 0.5로 설정하여 새롭게 적용되는 소스 노드의 사운드와 겹치게 만든다.
  162.                 if (this.currentGainNode) this.currentGainNode.gain.value = volume;
  163.  
  164.                 context = this.ctx;
  165.  
  166.                 var source = this.getTypeToSource(context, type);
  167.  
  168.                 this.currentGainNode = context.createGainNode();
  169.                 this.currentGainNode.connect(context.destination);
  170.  
  171.                 source.connect(this.currentGainNode);
  172.                 source.noteOn(0);
  173.  
  174.                 callback.call(context);
  175.  
  176.                 return this;
  177.  
  178.             },
  179.             getTypeToSource: function (context, type) {
  180.  
  181.                 var source = context.createBufferSource();
  182.  
  183.                 switch (type) {
  184.                     case 'bg1':
  185.                         {
  186.                             source.buffer = this.buffers[0];
  187.                             break;
  188.                         }
  189.                     case 'bg2':
  190.                         {
  191.                             source.buffer = this.buffers[1];
  192.                             break;
  193.                         }
  194.                     case 'bg3':
  195.                         {
  196.                             source.buffer = this.buffers[2];
  197.                             break;
  198.                         }
  199.                 }
  200.  
  201.                 return source;
  202.             },
  203.             sourceNormalEach: function (callback) {
  204.  
  205.                 context = this.ctx;
  206.                 buffers = this.buffers || [];
  207.                 callback = callback || function () { ; };
  208.  
  209.                 for (var i = 0, length = buffers.length; i < length; i++) {
  210.  
  211.                     // 사운드 소스 생성
  212.                     var source = context.createBufferSource();
  213.                     source.buffer = buffers[i];
  214.  
  215.                     // 소스를 스피커와 연결
  216.                     source.connect(context.destination);
  217.                     //callback
  218.                     callback.apply(context, [source, i + 1]);
  219.                 }
  220.  
  221.                 return this;
  222.             },
  223.             sourceGainNodeEach: function (callback) {
  224.  
  225.                 context = this.ctx;
  226.                 buffers = this.buffers || [];
  227.                 callback = callback || function () { ; };
  228.  
  229.                 for (var i = 0, length = buffers.length; i < length; i++) {
  230.  
  231.  
  232.                     var gainNode = context.createGainNode();
  233.                     // gainNode를 스피커와 연결
  234.                     gainNode.connect(context.destination);
  235.  
  236.                     var source = context.createBufferSource();
  237.                     source.buffer = buffers[i];
  238.                     // 소스를 생성된 gainNode 노드와 연결
  239.                     source.connect(gainNode);
  240.  
  241.                     //callback
  242.                     callback.apply(context, [source, gainNode, i + 1]);
  243.                 }
  244.  
  245.                 return this;
  246.             },
  247.             sourceCompressorEach: function (callback) {
  248.  
  249.                 context = this.ctx;
  250.                 buffers = this.buffers || [];
  251.                 callback = callback || function () { ; };
  252.  
  253.                 for (var i = 0, length = buffers.length; i < length; i++) {
  254.  
  255.                     var compressor = context.createDynamicsCompressor();
  256.                     compressor.connect(context.destination);
  257.  
  258.                     var gainNode = context.createGainNode();
  259.                     // gainNode를 Compressor와 연결
  260.                     gainNode.connect(compressor);
  261.  
  262.                     var source = context.createBufferSource();
  263.                     source.buffer = buffers[i];
  264.                     source.connect(gainNode);
  265.                     //callback
  266.                     callback.apply(context, [source, gainNode, compressor, i + 1]);
  267.                 }
  268.  
  269.                 return this;
  270.             },
  271.             sourceDelayNodeEach: function (callback) {
  272.  
  273.                 context = this.ctx;
  274.                 buffers = this.buffers || [];
  275.                 callback = callback || function () { ; };
  276.  
  277.                 for (var i = 0, length = buffers.length; i < length; i++) {
  278.  
  279.                     var delayNode = context.createDelayNode();
  280.                     delayNode.connect(context.destination);
  281.  
  282.                     var source = context.createBufferSource();
  283.                     source.buffer = buffers[i];
  284.                     source.connect(delayNode);
  285.                     //callback
  286.                     callback.apply(context, [source, delayNode, i + 1]);
  287.                 }
  288.  
  289.                 return this;
  290.             },
  291.             sourceFilterEach: function (callback) {
  292.  
  293.                 context = this.ctx;
  294.                 buffers = this.buffers || [];
  295.                 callback = callback || function () { ; };
  296.  
  297.                 for (var i = 0, length = buffers.length; i < length; i++) {
  298.  
  299.                     // createBiquadFilter를 사용해, 사운드 시그널의 주파수를 필터링해 주파수의 응답을 변경한다.
  300.                     var filter = context.createBiquadFilter();
  301.                     filter.connect(context.destination);
  302.  
  303.                     var source = context.createBufferSource();
  304.                     source.buffer = buffers[i];
  305.                     source.connect(filter);
  306.                     //callback
  307.                     callback.apply(context, [source, filter, i + 1]);
  308.                 }
  309.  
  310.                 return this;
  311.             },
  312.             sourcePannerEach: function (callback) {
  313.  
  314.                 context = this.ctx;
  315.                 buffers = this.buffers || [];
  316.                 callback = callback || function () { ; };
  317.  
  318.                 for (var i = 0, length = buffers.length; i < length; i++) {
  319.  
  320.                     // AudioPannerNode를 활용해 위치와 공간을 프로세싱 한다.
  321.                     var panner = context.createPanner();
  322.                     panner.connect(context.destination);
  323.  
  324.                     var source = context.createBufferSource();
  325.                     source.buffer = buffers[i];
  326.                     source.connect(panner);
  327.                     //callback
  328.                     callback.apply(context, [source, panner, i + 1]);
  329.                 }
  330.  
  331.                 return this;
  332.             },
  333.             sourceConvolverEach: function (callback) {
  334.  
  335.                 context = this.ctx;
  336.                 buffers = this.buffers || [];
  337.                 callback = callback || function () { ; };
  338.  
  339.                 for (var i = 0, length = buffers.length; i < length; i++) {
  340.  
  341.                     // 동일한 소리를 장소에 따라 다르게 녹음된 데이터를 생성한다.
  342.                     var convolver = context.createConvolver();
  343.                     // 임펄스 데이터
  344.                     convolver.buffer = buffers[i];
  345.                     convolver.connect(context.destination);
  346.  
  347.                     var source = context.createBufferSource();
  348.                     source.buffer = buffers[i];
  349.                     source.connect(convolver);
  350.                     //callback
  351.                     callback.apply(context, [source, convolver, i + 1]);
  352.                 }
  353.  
  354.                 return this;
  355.             }
  356.  
  357.         }
  358.  
  359.         WebAudio.fn.init.prototype = WebAudio.prototype;
  360.  
  361.  
  362.         function marge() {
  363.  
  364.             var target = this
  365.                 , opts = []
  366.                 , src = null
  367.                 , copy = null;
  368.  
  369.             for (var i = 0, length = arguments.length; i < length; i++) {
  370.  
  371.                 opts = arguments[i];
  372.  
  373.                 for (var n in opts) {
  374.  
  375.                     src = target[n];
  376.                     copy = opts[n];
  377.                     target[n] = copy;
  378.                 }
  379.             }
  380.  
  381.             return target;
  382.         }
  383.  
  384.         return WebAudio;
  385.  
  386.     })();
  387.  
  388.  
  389.     function init() {
  390.         BufferLoader({
  391.             urls: [
  392.                 '/sabo/bg1.mp3',
  393.                 '/sabo/bg2.mp3',
  394.                 '/sabo/bg3.mp3'
  395.             ],
  396.             callback: function (buffers) {
  397.  
  398.                 var ctx = this;
  399.                 var Audio = WebAudio({
  400.                     ctx: ctx,
  401.                     buffers: buffers
  402.                 });
  403.  
  404.  
  405.                 $('#BackgroundSoundPlay1').click(function (e) {
  406.                     Audio.play('bg1');
  407.                     return false;
  408.                 });
  409.  
  410.                 $('#BackgroundSoundPlay2').click(function (e) {
  411.                     Audio.play('bg2');
  412.                     return false;
  413.                 });
  414.  
  415.                 $('#BackgroundSoundPlay3').click(function (e) {
  416.                     Audio.play('bg3');
  417.                     return false;
  418.                 });
  419.  
  420.                 $('#BackgroundSoundCrossFadePlay1').click(function (e) {
  421.                     Audio.crossFadePlay('bg1', 0.5);
  422.                     return false;
  423.                 });
  424.  
  425.                 $('#BackgroundSoundCrossFadePlay2').click(function (e) {
  426.                     Audio.crossFadePlay('bg2', 0.5);
  427.                     return false;
  428.                 });
  429.  
  430.                 $('#BackgroundSoundCrossFadePlay3').click(function (e) {
  431.                     Audio.crossFadePlay('bg3', 0.5);
  432.                     return false;
  433.                 });
  434.  
  435.                 $('#sourceNormalEach').click(function (e) {
  436.                     Audio.sourceNormalEach(function (source, i) {
  437.  
  438.                         // 소스 반복 설정    
  439.                         //source.loop = true;
  440.                         source.noteOn(0);
  441.                     });
  442.  
  443.                     return false;
  444.                 });
  445.  
  446.                 $('#sourceGainNodeEach').click(function (e) {
  447.                     Audio.sourceGainNodeEach(function (source, gainNode, i) {
  448.  
  449.                         // 재생 볼륨 설정
  450.                         gainNode.gain.value = 0.2 * i;
  451.                         // 소스 반복 설정
  452.                         //source.loop = true;
  453.                         // 재생 시점이 다른 반복 재생
  454.                         source.noteOn(this.currentTime + i);
  455.                     });
  456.  
  457.                     return false;
  458.                 });
  459.  
  460.                 $('#sourceGainNodeScheduleEach').click(function (e) {
  461.                     Audio.sourceGainNodeEach(function (source, gainNode, i) {
  462.  
  463.                         //gainNode.gain.cancelScheduledValues(this.currentTime);
  464.  
  465.                         // 특정 시간까지 값을 리니어하게 변경하도록 스케줄링 한다.
  466.                         // Ramp up and down.
  467.                         gainNode.gain.linearRampToValueAtTime(1.0, (this.currentTime + 0.5));
  468.                         gainNode.gain.linearRampToValueAtTime(0.0, (this.currentTime + 1.5));
  469.                         gainNode.gain.linearRampToValueAtTime(1.0, (this.currentTime + 1.5));
  470.                         gainNode.gain.linearRampToValueAtTime(0.0, (this.currentTime + 2.0));
  471.                         gainNode.gain.linearRampToValueAtTime(1.0, (this.currentTime + 2.5));
  472.  
  473.                         source.noteOn(0);
  474.                     });
  475.  
  476.  
  477.  
  478.                     return false;
  479.                 });
  480.  
  481.                 $('#sourceDelayNodeEach').click(function (e) {
  482.                     Audio.sourceDelayNodeEach(function (source, delayNode, i) {
  483.                         // DelayNode를 사용하면 오디오 시그널의 통과를 지연시킨다.
  484.                         delayNode.delayTime.setValueAtTime(0, this.currentTime);
  485.  
  486.                         source.noteOn(0);
  487.                     });
  488.  
  489.                     return false;
  490.                 });
  491.  
  492.  
  493.                 $('#sourceFilterEach').click(function (e) {
  494.                     Audio.sourceFilterEach(function (source, filter, i) {
  495.  
  496.                         // LowPass
  497.                         filter.type = 0;
  498.                         filter.Q.vlaue = 12.06 + i;
  499.                         filter.frequency.value = 400;
  500.  
  501.                         source.noteOn(0);
  502.                     });
  503.  
  504.                     return false;
  505.                 });
  506.  
  507.  
  508.                 $('#sourcePannerEach').click(function (e) {
  509.                     Audio.sourcePannerEach(function (source, panner, i) {
  510.  
  511.                         panner.coneOuterGain = 0.5;
  512.                         panner.coneOuterAngle = 180;
  513.                         panner.coneinnerAngle = 0;
  514.  
  515.                         panner.setPosition(1, 10, 0);
  516.                         panner.setOrientation(20, 10, 5);
  517.                         panner.setVelocity(0, 1, 0);
  518.  
  519.                         source.noteOn(0);
  520.                     });
  521.  
  522.                     return false;
  523.                 });
  524.  
  525.                 $('#sourceConvolverEach').click(function (e) {
  526.                     Audio.sourceConvolverEach(function (source, convolver, i) {
  527.                         source.noteOn(0);
  528.                     });
  529.  
  530.                     return false;
  531.                 });
  532.             },
  533.             errcallback: function (e) {
  534.             }
  535.         }).loader();
  536.            
  537.     }
  538.        
  539.     window.addEventListener('load', init, false);
  540.  
  541. //]]>
  542. </script>
  543. </head>
  544. <body>
  545.  
  546. <div>Play</div>
  547. <a id="BackgroundSoundPlay1" href="#">BackgroundSoundPlay1</a>
  548. <a id="BackgroundSoundPlay2" href="#">BackgroundSoundPlay2</a>
  549. <a id="BackgroundSoundPlay3" href="#">BackgroundSoundPlay3</a><br /><br />
  550.  
  551. <div>Cross Fade Play</div>
  552. <a id="BackgroundSoundCrossFadePlay1" href="#">BackgroundSoundCrossFadePlay1</a>
  553. <a id="BackgroundSoundCrossFadePlay2" href="#">BackgroundSoundCrossFadePlay2</a>
  554. <a id="BackgroundSoundCrossFadePlay3" href="#">BackgroundSoundCrossFadePlay3</a><br /><br />
  555.  
  556. <div>여러 가지 동작</div>
  557. <a id="sourceNormalEach" href="#">sourceNormalEach</a><br /><br />
  558. <a id="sourceGainNodeEach" href="#">sourceGainNodeEach</a><br /><br />
  559. <a id="sourceGainNodeScheduleEach" href="#">sourceGainNodeScheduleEach</a><br /><br />
  560. <a id="sourceDelayNodeEach" href="#">sourceDelayNodeEach</a><br /><br />
  561. <a id="sourceFilterEach" href="#">sourceFilterEach</a><br /><br />
  562. <a id="sourcePannerEach" href="#">sourcePannerEach</a><br /><br />
  563. <a id="sourceConvolverEach" href="#">sourceConvolverEach</a><br /><br />
  564. </body>
  565. </html>




참고 사이트:


제2회 hello world 오픈세미나 Web Audio API-가능성엿보기:

http://www.slideshare.net/deview/2-hello-world-web-aaudioapi


SoftSynth:

http://www.softsynth.com/webaudio/gainramp.php


GETTING STARTED WITH WEB AUDIO API:

http://www.html5rocks.com/en/tutorials/webaudio/intro/




SqlLite Helper



SqlLite Helper


HTML5에서 제공되는 WEB DB인 SqlLite Helper Module을 아래 코드와 같이 작성해(간단히;;) 보았습니다.


  1. // SqlLite DB Helper
  2. var SqlLite = new (function () {
  3.  
  4.     var that = null;
  5.     var SqlLite = function () {
  6.  
  7.         this.db = null;
  8.         return this;
  9.     }
  10.  
  11.     SqlLite.fn = SqlLite.prototype = {
  12.         ready: function (name, callback, size, version, displayName) {
  13.  
  14.  
  15.             if (!name) return false;
  16.  
  17.             callback = typeof callback === 'function' ? callback : function () { ; };
  18.  
  19.             // options
  20.             size = size || 5 * (1024 * 1024);
  21.             version = version || '1.0';
  22.             displayName = displayName || 'sqlLiteDB';
  23.  
  24.             var that = this;
  25.             if (window.openDatabase) {
  26.                 this.db = window.openDatabase(String(name), version, displayName, size, function (db) {
  27.                     callback.apply(that, Array.prototype.slice.call(arguments))
  28.                 });
  29.             }
  30.  
  31.             return this.db;
  32.         },
  33.         reader: function (sql, callback, errcallback) {
  34.  
  35.             if (!sql) return false;
  36.  
  37.             callback = typeof callback === 'function' ? callback : function () { ; };
  38.             errcallback = typeof errcallback === 'function' ? errcallback : function () { ; };
  39.  
  40.             var that = this;
  41.             this.db.readTransaction(function (ts) {
  42.                 ts.executeSql(sql, [], function (ts, ds) {
  43.                     callback.call(that, ds);
  44.                     delete ts;
  45.                 }, function (e) {
  46.                     errcallback.apply(that, [e, e.message]);
  47.                     delete ts;
  48.                 });
  49.             });
  50.  
  51.             return this;
  52.         },
  53.         execute: function (sql, datas, callback, errcallback) {
  54.  
  55.             if (!sql) return false;
  56.  
  57.             callback = typeof callback === 'function' ? callback : function () { ; };
  58.             errcallback = typeof errcallback === 'function' ? errcallback : function () { ; };
  59.  
  60.             var that = this;
  61.             this.db.transaction(function (ts) {
  62.                 ts.executeSql(sql, datas, function (ts) {
  63.                     callback.call(that);
  64.                     delete ts;
  65.                 }, function (e) {
  66.                     errcallback.apply(that, [e, e.message]);
  67.                     delete ts;
  68.                 });
  69.             });
  70.  
  71.             return this;
  72.         }
  73.     }
  74.  
  75.     return SqlLite;
  76.  
  77. } ())();



SqlLite.ready(name, callback, size, version, displayName) 함수


name: DB명

callback: 완료 시 handler method

size: DB Size

version: DB Version

displayName: displayName


  1. SqlLite.ready('testDB', function () {
  2.  
  3.     var sql = [];
  4.  
  5.     sql.push(' create table if not exists comments');
  6.     sql.push(' (');
  7.     sql.push(' idx integer primary key autoincrement,');
  8.     sql.push(' saboidx integer not null,');
  9.     sql.push(' menuidx integer not null,');
  10.     sql.push(' boardidx integer not null,');
  11.     sql.push(' usn text integer null,');
  12.     sql.push(' uid text not null,');
  13.     sql.push(' uc text not null,');
  14.     sql.push(' ucn text not null,');
  15.     sql.push(' up text not null,');
  16.     sql.push(' upn text not null,');
  17.     sql.push(' comments text not null,');
  18.     sql.push(' isdel integer not null,');
  19.     sql.push(' regdate text not null');
  20.     sql.push(' )');
  21.  
  22.     this.execute(sql.join(''));
  23.  
  24. });



SqlLite.reader(sql, callback, errcallback) 함수


sql: Sql쿼리문

callback: 완료 시 handler method

errcallback: 에러 시 handler method


  1. SqlLite.reader('select * from comments order by idx desc limit (' + pageSize + ') offset ' + pageSize + ' * ' + (page - 1),
  2. function(ds){
  3.  
  4.     for (var i = 0, length = ds.rows.length; i < length; i++) {
  5.  
  6.         var idx = ds.rows.item(i).idx
  7.             , usn = ds.rows.item(i).usn
  8.             , uid = ds.rows.item(i).uid
  9.             , comments = ds.rows.item(i).comments
  10.             , regdate = ds.rows.item(i).regdate;
  11.  
  12.         h.push('<tr>');
  13.         h.push('<td>' + idx + '</td>');
  14.         h.push('<td>' + usn + '</td>');
  15.         h.push('<td>' + uid + '</td>');
  16.         h.push('<td>' + comments + '</td>');
  17.         h.push('<td>' + regdate + '</td>');
  18.         h.push('<td><input type="button" onclick="return boardDelete(' + idx + ', ' + page + ')" value="삭제" /></td>');
  19.         h.push('</tr>');
  20.     }
  21.  
  22.     elem.innerHTML = h.join('');
  23.  
  24.     SqlLite.reader('select count(idx) as cnt from comments', function (ds) {
  25.         paging(page, ds.rows.item(0).cnt);
  26.     });
  27. });



SqlLite.execute(sql, datas, callback, errcallback) 함수


sql: Sql쿼리문

datas: 파라메터 배열

callback: 완료 시 handler method

errcallback: 에러 시 handler method


  1. SqlLite.execute('insert into comments (saboidx, menuidx, boardidx, usn, uid, uc, ucn, up, upn, comments, isdel, regdate) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
  2. [SaboIdx, MenuIdx, BoardIdx, Usn, Uid, Uc, Ucn, Up, Upn, Comments, IsDel, Regdate],
  3. function () {
  4.     boardList(pageSize);
  5. });



참고 사이트:

http://www.w3.org/TR/webdatabase/#databases



HTML5 API - APPCACHE



HTML5 APPCACHE 방식의 최대 장점으로는 오프라인 작업 작업을 구현할 수 있다는 것입니다.


즉, 데스크탑 및 모바일 기기의 통신이 끊어진 경우에도 어플리케이션 작업이 가능하다는 말이며,


이를 통해 모바일과 같은 환경(네트웍 속도가 느린..)의 사용자는 불필요한 HTTP 요청(이미 캐쉬 되어 있으므로 304(조건부 재요청)를 일으키지 않는다.)에 대한 네트웍 비용을 줄임으로써 좀 더 빠른 응답을 받아볼 수 있을 것입니다.





더 자세한 설명은 API 작성 시 참조한 링크를 대신합니다.




1. APPCACHE API 코드:


  1. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="m.aspx.cs" Inherits="FPSCamp.Potal.mobile.m" %>
  2. <!DOCTYPE HTML>
  3. <html manifest="cache.manifest">
  4. <head>
  5.     <title>Manifest</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. //<![CDATA[
  10.    
  11.     // onLine, offLine
  12.     var Connection = (function (win, doc) {
  13.  
  14.         return new (function () {
  15.  
  16.             function init() {
  17.                 return this;
  18.             };
  19.  
  20.             init.prototype = {
  21.                 isOnline: function () {
  22.                     return win.navigator.onLine;
  23.                 }
  24.             };
  25.  
  26.             return init;
  27.         } ());
  28.  
  29.     })(window, document);
  30.  
  31.     // appCache
  32.     var Cache = (function (win, doc) {
  33.  
  34.         return function (opt) {
  35.  
  36.             function init() {
  37.  
  38.                 Connection.isOnline() && win.applicationCache && (function () {
  39.  
  40.                     this.cache = win.applicationCache;
  41.  
  42.                     // events
  43.                     this.onchecking = function () { ; };
  44.                     this.onnoupdate = function () { ; };
  45.                     this.onupdateready = function () { ; };
  46.                     this.onobsolete = function () { ; };
  47.                     this.ondownloading = function () { ; };
  48.                     this.oncached = function () { ; };
  49.                     this.onerror = function () { ; };
  50.  
  51.                     //add evnets
  52.                     appendEvent.call(this);
  53.  
  54.                     extend.call(this, opt);
  55.  
  56.                 }).call(this);
  57.  
  58.                 return this;
  59.             };
  60.  
  61.             init.prototype =
  62.             {
  63.                 update: update,
  64.                 getStatus: getStatus
  65.             };
  66.  
  67.             return new init();
  68.  
  69.         };
  70.  
  71.  
  72.         function appendEvent() {
  73.  
  74.             var that = this;
  75.  
  76.             bind(this.cache, 'checking', function (e) { that.onchecking.apply(that, [e]); });
  77.             bind(this.cache, 'noupdate', function (e) { that.onnoupdate.apply(that, [e]); });
  78.             bind(this.cache, 'updateready', function (e) { that.onupdateready.apply(that, [e]); });
  79.             bind(this.cache, 'obsolete', function (e) { that.onobsolete.apply(that, [e]); });
  80.             bind(this.cache, 'downloading', function (e) { that.ondownloading.apply(that, [e]); });
  81.             bind(this.cache, 'cached', function (e) { that.oncached.apply(that, [e]); });
  82.  
  83.             // exception
  84.             bind(this.cache, 'error', function (e) { that.onerror.apply(that, [e]); });
  85.         };
  86.  
  87.         function update() {
  88.             try {
  89.                 Connection.isOnline() && this.cache.update();
  90.             }
  91.             catch (e) {
  92.                 this.cache.onerror();
  93.             }
  94.         };
  95.  
  96.         function getStatus() {
  97.             return getStatusText(this.cache.status);
  98.         };
  99.  
  100.         function getStatusText(status) {
  101.             return ['uncached', 'idle', 'checking', 'downloading', 'updateready', 'obsolete'][status];
  102.         };
  103.  
  104.  
  105.         // 객체 상속 함수
  106.         function extend() {
  107.  
  108.             var target = this
  109.           , opts = []
  110.           , src = null
  111.           , copy = null;
  112.  
  113.             for (var i = 0, length = arguments.length; i < length; i++) {
  114.  
  115.                 opts = arguments[i];
  116.  
  117.                 for (var n in opts) {
  118.                     n = n.toLowerCase();
  119.                     src = target[n];
  120.                     copy = opts[n];
  121.  
  122.                     if (src === copy) continue;
  123.                     if (copy) target[n] = copy;
  124.                 }
  125.             }
  126.         };
  127.  
  128.         return init;
  129.  
  130.     })(window, document);
  131.  
  132.     var app = Cache({
  133.         onchecking: function () {
  134.             alert('checking');
  135.         },
  136.         onnoupdate: function () {
  137.             alert('noupdate');
  138.         },
  139.         onupdateready: function () {
  140.             alert('cheupdatereadycking');
  141.         },
  142.         onobsolete: function () {
  143.             alert('obsolete');
  144.         },
  145.         ondownloading: function () {
  146.             alert('downloading');
  147.         },
  148.         oncached: function () {
  149.             alert('cached');
  150.         }
  151.     });
  152.  
  153.     // 이벤트 할당
  154.     function bind(elem, type, handler, capture) {
  155.         type = typeof type === 'string' && type || '';
  156.         handler = handler || function () { ; };
  157.  
  158.         if (elem.addEventListener) {
  159.             elem.addEventListener(type, handler, capture);
  160.         }
  161.         else if (elem.attachEvent) {
  162.             elem.attachEvent('on' + type, handler);
  163.         }
  164.  
  165.         return elem;
  166.     };
  167.  
  168.  
  169. //]]>
  170. </script>
  171. <input type="button" value="appUpdate" onclick="app.update()" />
  172. <img src="1.jpg" alt="1" title="1" />
  173. </body>
  174. </html>





2. manifest 파일을 위한 IIS Content-Type 등록:


 

위와 같이 해당 사이트의 등록정보를 수정합니다.





3. Cache 등록 화면:



위와 같이 해당 menifest 파일이 등록되었으며, 그에 따른 이벤트들이 각가 실행되고 있다.





결과:


브라우저 지원차이 등의 여러 가지 테스트를 대부분의 표준 브라우저에서 시도했지만, 각 브라우저에 대한 결과만 조금씩 달랐을 뿐, 네트웍이 분리된 상태의 작업(오프라인 작업)은 가능하지 않았습니다. 정확히 말해 manifest에 등록된 Offline 리소스들을 가져오지 못하는 문제였습니다.;;;



이 기능에 대한 테스트는 여기서 일단락 지을 것이며, 만약 정확한 테스트를 마치신 분이 계신다면, 댓글 남겨주시면 감사하겠습니다.  꾸벅~~ ^^;;





참고 사이트:


@clearboth:

http://html5.clearboth.org/offline.html


@박종명님:

http://m.mkexdev.net/60






HTML5 API - LocalStorage, SessionStorage



기존 쿠키 방식과 더불어 HTML5는 로컬에 메모리 데이터를 담는 localStorge 방식과 브라우저 메모리를 활용하는 sessionStorage 방식을 지원합니다.


먼저 localStorage 방식은 용량이 제한(보통 4KB)적인 쿠키 방식에 비해 문서나 메일과 같은 덩치 큰 데이터를 수용할 수 있는 방식이며, 객체({}) 타입의 메모리 할당과 오프라인 참조까지 지원하는 구조입니다.


이 방식의 데이터 생명 주기는 모든 브라우저 창 종료 시에도 유지되며, 같은 도메인에서 실행되는 모든 브라우저 창과 모든 탭을 통해 데이터가 공유됩니다.



두 번째로 sessionStorage 방식은 데이터 생명 주기를 제외한 대부분의 기능들은 localStorage 방식과 동일합니다.


이 방식의 데이터 생명 주기는 데이터가 저장된 브라우저 창 또는 탭에서만 해당 값이 유지됩니다. 즉, localStorage 방식과 달리 모든 브라우저 창과 탭에서 공유되지 않습니다.





1. 브라우저 지원현황:



브라우저 지원현황:

http://caniuse.com/#feat=namevalue-storage






아래는 두 가지 Storage 방식을 모듈화 한 API 코드입니다.


  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4.     <title>Storage</title>
  5. </head>
  6. <body>
  7. <script type="text/javascript">
  8. //<![CDATA[
  9.  
  10.     var Storage = (function (win, doc) {
  11.  
  12.         return function (type, onStorage) {
  13.  
  14.             function init() {
  15.  
  16.                 this.type = type || 'session';
  17.                 this.storage = win[this.type === 'local' ? 'localStorage' : 'sessionStorage'];
  18.                 this.length = this.storage.length;
  19.  
  20.                 // events
  21.                 this.onStorage = onStorage || function () { ; };
  22.  
  23.                 //add evnets
  24.                 appendEvent.call(this);
  25.  
  26.                 return this;
  27.             };
  28.  
  29.             init.prototype =
  30.             {
  31.                 set: set,
  32.                 get: get,
  33.                 remove: remove,
  34.                 removeAll: removeAll
  35.             };
  36.  
  37.             return new init;
  38.         };
  39.  
  40.         function appendEvent() {
  41.             var that = this;
  42.  
  43.             // storage 이벤트는 다른 document 기준에서 동일키 값 변경 시 발생된다.
  44.             // (자기 document 안에서는 발생이 되지않는다. 즉, 탭을 하나 열고 테스트한다.)
  45.             bind(window, 'storage', function (e) {
  46.                 that.onStorage.apply(that, [e, that.type, e.url, e.key, e.oldValue, e.newValue]);
  47.             });
  48.         };
  49.  
  50.         // 스토리지 key value 생성
  51.         function set(key, value) {
  52.  
  53.             key = key || '';
  54.             // JSON 객체담기
  55.             value = value && value.constructor === Object ? JSON.stringify(value) : value;
  56.  
  57.             this.storage.setItem(key, value);
  58.             this.length = this.storage.length;
  59.  
  60.             return this;
  61.         };
  62.  
  63.         // 스토리지 key value 가져오기
  64.         function get(key) {
  65.            
  66.             var item = this.storage.getItem(key);
  67.             try {
  68.                 // JSON 객체 가져오기
  69.                 return JSON.parse(item).constructor === Object && JSON.parse(item);
  70.             }
  71.             catch (e) {
  72.                 return item;
  73.             }
  74.         };
  75.  
  76.         // 스토리지 삭제
  77.         function remove(key) {
  78.             this.storage.removeItem(key);
  79.  
  80.             this.length = this.storage.length;
  81.             return this;
  82.         };
  83.  
  84.         // 스토리지 전체 삭제
  85.         function removeAll() {
  86.             this.storage.clear();
  87.  
  88.             this.length = this.storage.length;
  89.             return this;
  90.         };
  91.  
  92.     })(window, document);
  93.  
  94.    
  95.    
  96.     // 로컬 스토리지 생성
  97.     var local = Storage('local', function (e, type, url, oldValue, newValue) { alert(e); });
  98.    
  99.     // 세션 스토리지 생성
  100.     var session = Storage('session', function (e, type, url, oldValue, newValue) { alert(e); });
  101.  
  102.     function add() {
  103.  
  104.         local.set('key', 'value1');
  105.         session.set('key', 'value1');
  106.     };
  107.  
  108.     function modify() {
  109.    
  110.         local.set('key', 'value2');
  111.         session.set('key', {'value2': 'value2'});
  112.     };
  113.  
  114.     function get(){
  115.  
  116.         alert(local.get('key'));
  117.         alert(session.get('key').value2);
  118.     };
  119.  
  120.     function remove() {
  121.  
  122.         local.remove('key');
  123.         session.remove('key');
  124.     };
  125.  
  126.     function removeAll() {
  127.  
  128.         local.removeAll();
  129.         session.removeAll();
  130.     };
  131.  
  132.  
  133.     function length() {
  134.         alert('local-length:' + local.length + ',' + 'session-length:' + session.length);
  135.     }
  136.  
  137.  
  138.     // 이벤트 할당
  139.     function bind(elem, type, handler, capture) {
  140.         type = typeof type === 'string' && type || '';
  141.         handler = handler || function () { ; };
  142.  
  143.         if (elem.addEventListener) {
  144.             elem.addEventListener(type, handler, capture);
  145.         }
  146.         else if (elem.attachEvent) {
  147.             elem.attachEvent('on' + type, handler);
  148.         }
  149.  
  150.         return elem;
  151.     };
  152.  
  153.  
  154. //]]>
  155. </script>
  156. <input type="button" value="storageAppend" onclick="add()" />
  157. <input type="button" value="storageModify" onclick="modify()" />
  158. <input type="button" value="storageGet" onclick="get()" />
  159. <input type="button" value="storageRemove" onclick="remove()" />
  160. <input type="button" value="storageRemoveAll" onclick="removeAll()" />
  161. <input type="button" value="storageLength" onclick="length()" />
  162. </body>
  163. </html>




실행 결과:






HTML5 API - Web Worker



웹 워커는 어플리케이션에서 백그라운드 프로세서를 사용할 수 있게 해주며, 기존 단일 레드에서 벗어나 멀티 레드를 통해 작업하는 것이 가능합니다.


하지만 워커는 DOM 전역 객체인 window 객체에 접근하지 못하며. 이는 곧 DOM에 접근하지 못한다는 말과 같습니다.


또한, 설계상 UI 작업(스레드)에 영향을 미치지는 않지만, 과도한 작업 시 CPU를 점유량 때문에 시스템 속도를 느리게 만듭니다.




1. 브라우저 지원현황:



브라우저 지원현황:

http://caniuse.com/#feat=webworkers



- 아래는 총 3개의 백그라운드 레드(워커)와 postMessage API를 연동하여 워커와 데이터를 주고받는 코드 예제 입니다.


  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4.     <title>postMessage</title>
  5. </head>
  6. <body>
  7. <script type="text/javascript">
  8. //<![CDATA[
  9.  
  10.     var ws = {};
  11.    
  12.     function start(id) {
  13.  
  14.         (!ws[id] && window.Worker) && function () {
  15.            
  16.             //워커 생성
  17.             var w = ws[id] = new Worker('workers.js?2')
  18.  
  19.             // 워커 이벤트 핸들러 할당
  20.             bind(w, 'message', function (e) { document.getElementById(id).value = e.data; });
  21.             bind(w, 'error', function (e) { alert(e.message); });
  22.  
  23.             w.postMessage();
  24.         }();
  25.     };
  26.  
  27.     function terminate(id) {
  28.         // 워커 중지
  29.         ws[id].terminate();
  30.         delete ws[id];
  31.     };
  32.  
  33.     var init = (function (fn) {
  34.         fn('w1');
  35.         fn('w2');
  36.         fn('w3');
  37.     })(start);
  38.  
  39.     // 이벤트 할당
  40.     function bind(elem, type, handler, capture) {
  41.         type = typeof type === 'string' && type || '';
  42.         handler = handler || function () { ; };
  43.  
  44.         if (elem.addEventListener) {
  45.             elem.addEventListener(type, handler, capture);
  46.         }
  47.         else if (elem.attachEvent) {
  48.             elem.attachEvent('on' + type, handler);
  49.         }
  50.  
  51.         return elem;
  52.     };
  53.  
  54.  
  55. //]]>
  56. </script>
  57. <input id="w1" style="width:300px" />
  58. <input id="w2" style="width:300px" />
  59. <input id="w3" style="width:300px" /><br />
  60. <input type="button" name="w1" value="woker1Start" onclick="start(this.name)" />
  61. <input type="button" name="w1" value="woker1Stop" onclick="terminate(this.name)" /><br />
  62. <input type="button" name="w2" value="woker2Start" onclick="start(this.name)" />
  63. <input type="button" name="w2" value="woker2Stop" onclick="terminate(this.name)" /><br />
  64. <input type="button" name="w3" value="woker3Start" onclick="start(this.name)" />
  65. <input type="button" name="w3" value="woker3Stop" onclick="terminate(this.name)" />
  66. </body>
  67. </html>




참고 사이트:


webWorker API:

http://www.slideshare.net/jidolstar/html5-web-worker




HTML5 API - FORM



HTML5 FORM에서는 사용자 접근성 향상을 위해 기존 컨트롤보다 더 많은 종류의 컨트롤(기존 자바스크립트 + CSS를 활용하여 구현된 컨트롤 등)들이 대거 추가되었습니다.


즉, 표준으로 지원하지 않는 모든 컨트롤들은 각 어플리케이션 환경에 따라 사용자가 직접 만들어 사용하였습니다.




1. 브라우저 지원현황:


HTML5 FORM을 지원하는 브라우저가 점차 많아지고 있는 것이 현실이지만, 아직 까지는 많이 미흡한 편이며, 최근 웹킷(WebKit)기반 브라우저에서도 폼에 대한 지원을 강화하고 있는 추세입니다.(모바일 웹 브라우저 기준)




브라우저 지원현황:

http://caniuse.com/#feat=form-validation





- 아래는 HTML5 FORM 코드 예제이며, 컨트롤 지원 형태는 웹과 모바일 브라우저가 다르다는 것을 명심하시기 바라며, 프로젝트 적용 시 해당 컨트롤에 대한 브라우저 지원사항을 꼼꼼히 테스트하시기 바랍니다.



2. 마크업 예제:


  1. <form id="html5Form" name="html5Form" action="">
  2. <!-- input -->
  3. <input type="text" id="input_text" name="input_text" placeholder="input_text" autocomplete="on" required="true" autofocus/><br />
  4. <input type="tel" id="input_tel" placeholder="input_tel" autocomplete="off" /><br />
  5. <input type="email" id="input_email" placeholder="input_email" /><br />
  6. <input type="url" id="input_url" placeholder="input_url" /><br />
  7. <input type="search" id="input_search" placeholder="input_search" /><br />
  8. <input type="range" id="input_range" min="0" max="100" value="0" step="5" /><br />
  9. <input type="number" id="input_number" placeholder="input_number" /><br />
  10. <input type="color" id="input_color" placeholder="input_color" /><br />
  11. <input type="datetime" id="input_datetime" placeholder="input_datetime" /><br />
  12. <input type="datetime-local" id="input_datetime-local" placeholder="input_datetime-local" /><br />
  13. <input type="time" id="input_time" placeholder="input_time" /><br />
  14. <input type="date" id="input_date" placeholder="input_date" /><br />
  15. <input type="week" id="input_week" placeholder="input_week" /><br />
  16. <input type="month" id="input_month" placeholder="input_month" /><br />
  17. <meter min="0" max="100" value="50"></meter>
  18. <input type="submit" value="submit" />
  19. </form>




- 웹 브라우저:


거의 대 부분의 컨트롤이 지원하지 않습니다.



- 모바일 브라우저:


대부분의 컨트롤이 지원하며, 특히, 모바일 디바이스의 스마트 키보드(컨트롤 타입에 따라 키보드 배열이 달라진다)를 통해 사용자 접근성이 향상됩니다.




1. 전화(input_tel) 컨트롤:







2. 날짜(input_date) 컨트롤:






3. 주소(input_url) 컨트롤:








3. 유효성 검사 예제


  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4.     <title>postMessage</title>
  5. </head>
  6. <body>
  7. <script type="text/javascript">
  8. //<![CDATA[
  9.  
  10. bind(window, 'load', function () {
  11.  
  12.     bind(document.html5Form.input_text, 'invalid', function (e) {
  13.         var e = e.srcElement.validity;
  14.         // 정해진 유효성 검사에 대한 결과(성공: true / 실패: false)
  15.         alert(e.valid);
  16.         // 컨트롤에 반드시 값이 지정되게 하며, 비어있다면 true를 반환한다.
  17.         // required 속성을 지정하여 사용한다.
  18.         alert(e.valueMissing);
  19.         // 지정된 컨트롤의 패턴이 부합되지 않으면, true를 반환한다.
  20.         // Pattern 속성을 지정하여 사용한다.
  21.         alert(e.patternMismatch);
  22.         // 지정된 컨트롤의 값에 너무 많은 문자가 들어가면 true를 반환한다.
  23.         // maxLength 속성을 지정하여 사용한다.
  24.         alert(e.tooLong);
  25.         alert(e.validationMessage);
  26.  
  27.     });
  28.  
  29.     bind(document.html5Form.input_tel, 'invalid', function (e) {
  30.         var e = e.srcElement.validity;
  31.         // 정해진 유효성 검사에 대한 결과(성공: true / 실패: false)
  32.         alert(e.valid);
  33.     });
  34.  
  35.     bind(document.html5Form.input_email, 'invalid', function (e) {
  36.         var e = e.srcElement.validity;
  37.         // 정해진 유효성 검사에 대한 결과(성공: true / 실패: false)
  38.         alert(e.valid);
  39.         // 지정된 유형에 맞는 값인지 확인하며, 실패 시 true를 반환한다.
  40.         // 즉, 이메일 폼 같은 경우는 "@" 문자열이 반드시 포함되어야 한다.
  41.         alert(e.typeMismatch);
  42.  
  43.     });
  44. });
  45.  
  46.  
  47. // 이벤트 할당dasdad
  48. function bind(elem, type, handler, capture) {
  49.     type = typeof type === 'string' && type || '';
  50.     handler = handler || function () { ; };
  51.  
  52.     if (elem.addEventListener) {
  53.         elem.addEventListener(type, handler, capture);
  54.     }
  55.     else if (elem.attachEvent) {
  56.         elem.attachEvent('on' + type, handler);
  57.     }
  58.  
  59.     return elem;
  60. };
  61.  
  62.  
  63. //]]>
  64. </script>
  65. <form id="html5Form" name="html5Form" action="" onsubmit="return false">
  66. <!-- input -->
  67. <input type="text" id="input_text" name="input_text" placeholder="input_text" autocomplete="on" Pattern="([\w])+" maxlength="5" required /><br />
  68. <!-- 아래 컨트롤의 formnovalidate 속성으로 유효성 검사를 무시할 수 있다. -->
  69. <input type="text" id="Text1" name="input_text" placeholder="input_text" autocomplete="on" Pattern="([\w])+" maxlength="5" formnovalidate="formnovalidate" /><br />
  70. <input type="tel" id="input_tel" name="input_tel" placeholder="input_tel" autocomplete="off" Pattern="([0-9])+" required /><br />
  71. <input type="email" id="input_email" name="input_email" placeholder="input_email" required /><br />
  72.  
  73. <!-- input -->
  74. <!-- submit -->
  75. <input type="submit" value="submit" />
  76. <!-- submit -->
  77. </form>
  78. </body>
  79. </html>




실행 결과:






참고 사이트:


@Clearboth HTML5 스펙:

http://www.clearboth.org


HTML5 데모:

http://html5demos.com/



웹에서 실시간 데이터 처리하기(WebSocket, Comet)



HTML5 WebSocket웹에서 양 방향 통신을 지원하며, 그에 따른 실시간 서비스를 구현하기에 적합한 기술입니다.


또한, HTTP 실시간 통신 방식(COMET)인 폴링(Polling), 롱폴링(Long Polling), 스트리밍(Streaming) 방식보다 월등한 성능 차이를 보여줍니다.



기존 HTTP 실시간 통신 방식(COMET):



Ajax API 소스 코드는 이전 포스트를 참조하십시오.




1. 폴링(Polling) 방식:


- 브라우저가 일정한 주기로 HTTP 요청을 보내는 방식입니다.


보통 실시간 데이터의 업데이트 주기는 예측하기 어려우므로, 그에 따른 불필요한 서버 및 네트웍 부하가 늘어납니다.


즉, 불필요한 서버 요청이 다수 생긴다는 말입니다.




- 클라이언트 코드:


  1. function ajax() {
  2.  
  3.     Ajax.request({
  4.         url: 'http://m.fpscamp.com/m.aspx',
  5.         type: 'text',
  6.         method: 'post',
  7.         headers: {
  8.             'content-type': 'application/x-www-form-urlencoded'
  9.         },
  10.         data: {
  11.         },
  12.         onprogress: function (e, total, loaded, per, computable) {
  13.         },
  14.         onerror: function () {
  15.             alert('onerror');
  16.         },
  17.         callback: function (data, status) {
  18.             document.body.innerHTML = data;
  19.         }
  20.     });
  21. };
  22.  
  23. var timer = window.setInterval(function () { ajax(); }, 1000);


위 설명과 같이 window.setInterval() 메서드를 사용하여 브라우저에서 일정한 주기로 HTTP 요청을 서버로 보냅니다.




- 서버 코드(ASP.NET):


  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.UI;
  6. using System.Web.UI.WebControls;
  7. using System.Threading;
  8. using System.Net;
  9. using System.Net.Sockets;
  10.  
  11.  
  12. namespace FPSCamp.Potal.mobile
  13. {
  14.     public partial class m : System.Web.UI.Page
  15.     {
  16.         protected void Page_Load(object sender, EventArgs e)
  17.         {
  18.             Response.Write(DateTime.Now);
                Response.End();
  19.         }
  20.     }
  21. }





2. 롱폴링(Long Polling) 방식:


- HTTP 요청 시 서버는 해당 요청을 일정 시간 동안 대기 시킵니다. 만약, 대기 시간 안에 데이터가 업데이트되었다면, 그 즉시 클라이언트에게 응답을 보내고 전달받은 데이터를 처리 후 서버로 재요청을 시작합니다.


- 데이터 업데이트가 빈번한 경우엔 폴링에 비해 성능상 이점이 크지 않습니다.




- 클라이언트 코드:


  1. function ajax() {
  2.    
  3.     Ajax.request({
  4.         url: 'http://m.fpscamp.com/m.aspx',
  5.         type: 'text',
  6.         method: 'post',
  7.         headers: {
  8.             'content-type': 'application/x-www-form-urlencoded'
  9.         },
  10.         data: {
  11.         },
  12.         onprogress: function (e, total, loaded, per, computable) {
  13.             alert(e); // 이벤트
  14.             alert(total); // 전체 데이터 크기
  15.             alert(loaded); // 전송된  데이터 크기
  16.             alert(per); // 전송된 데이터 크기(percent)
  17.             alert(computable); // 전체 데이터 크기를 알고 있는지 여부
  18.         },
  19.         onerror: function () {
  20.             alert('onerror');
  21.         },
  22.         callback: function (data, status) {
  23.             // 응답 데이터 가공
  24.             document.body.innerHTML = data;
  25.             // 재 요청
  26.             ajax();
  27.         }
  28.     });};


callback() 메서드에서 응답 데이터를 가공 후 재 요청한다.




- 서버 코드(ASP.NET):


  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.UI;
  6. using System.Web.UI.WebControls;
  7. using System.Threading;
  8. using System.Net;
  9. using System.Net.Sockets;
  10.  
  11.  
  12. namespace FPSCamp.Potal.mobile
  13. {
  14.     public partial class m : System.Web.UI.Page
  15.     {
  16.         private DateTime start = DateTime.Now;
  17.         private DateTime? interval = null;
  18.         protected void Page_Load(object sender, EventArgs e)
  19.         {
  20.             this.interval = start.AddMinutes(10);
  21.  
  22.             while (DateTime.Now < this.interval)
  23.             {
  24.                 if (this.isUpdate())
  25.                 {
  26.                     Response.Write(DateTime.Now);
  27.                     Response.OutputStream.Flush();
  28.                     break;
  29.                 }
  30.             }
  31.         }
  32.         private bool isUpdate()
  33.         {
  34.             // 서버 업데이트 주기
  35.             return false;
  36.         }
  37.     }
  38. }


위 코드는 대기시간(this,inerval)을 10분으로 잡았으므로 데이터의 업데이트가 없어도 10분(대기시간)마다 연결을 끊어 버립니다.





3. 스트리밍(Streaming) 방식:


- 서버는 지속적인 업데이트를 위해 무한정(또는 일정 시간 동안) 요청을 대기시키며, "chunked" 기반 메시지를 이용하여 응답 시 연결을 유지 시킵니다.




스트리밍 방식에 대한 예제는 아래 링크로 대체 합니다.


- COMET을 이용한 웹 채팅 만들기:

http://hoons.kr/Lecture/LectureMain.aspx?BoardIdx=38734&kind=11&view=0




정리:


결국, COMET 방식은 데이터 전송 과정(요청/응답)에서 불필요한 많은 양의 네트웍 오버 헤더를 발생 시키며, 연결(HTTP 요청) 대기 시간에 따른 성능 저하도 추가로 발생합니다.


또한, 단방향(반이중 통신)통신인 HTTP 방식을 양방향 통신(이중 통신)으로 구현하기 위한 별도의 복잡한 개발 비용도 발생합니다.







HTML5 WebSocket:


- 이전 HTTP 통신과는 달리 클라이언트와 서버 간 양방향 통신이 가능하며, 이때 네트웍상의 메시지는 0X00바이트로 시작해서 0xFF 바이트로 끝나고 그 사이에는 UTF-8 데이터가 채워지게 됩니다.




- 아래는 폴링(Polling) 방식과 WebSocket 방식의 불필요한 네트워크 오버헤드를 비교한 결과입니다.




1. 폴링 방식:


- HTTP 요청/응답 시마다 아래와 같은 HTTP 헤더 데이터들이 전달되며, 그에 따른 네트웍 오버헤드가 발생한다.





2. WebSocket 방식:


- WebSocket 방식은 HTTP 방식과 달리 불필요한 요청/응답 헤더 데이터가 존재하지 않습니다.






3. 네트워크 오버헤드 비교:



폴링(Polling) 방식:


요청/응답 헤더 데이터 용량: (871 Byte)


1. 1000명 *  헤더 데이터 용량  = 871,000  Byte

2. 10000 *  헤더 데이터 용량  = 8,710,000  Byte

3. 100000 *  헤더 데이터 용량  = 87,100,000 Byte




WebSocket 방식:


메시지 데이터 용량: (2 Byte)


1. 1000 *   메시지 데이터 용량   = 2,000  Byte

2. 10000 *   메시지 데이터 용량   = 20,000  Byte

3. 100000 *   메시지 데이터 용량   = 200,000 Byte



테스트 결과:


위 테스트 결과처럼 폴링 방식 데이터(위의 경우 header 데이터만 있으며, Body가 빠진상태)처리는 WebSocket 처리 방식보다 주고 받는 데이터양에 의해 상당한 네트워크 오버헤드가 발생합니다.







4. 전송 대기시간 비교:



폴링(Polling) 방식:


서버 응답 후 요청에 대한 대기시간이 추가로 발생합니다.


즉, 클라이언트 요청부터 서버 응답까지 50ms 걸렸으며, 서버 응답 후 재요청을 만들기 위한 추가적인 대기시간이 걸립니다.




WebSocket 방식:


최초 서버와 연결 후, 연결이 그대로 유지되므로 클라이언트가 재요청을 보낼 필요가 없습니다. 


즉 추가적인 대기시간이 발생하지 않습니다.



테스트 결과:


위 테스트 결과처럼 연결이 유지되는 WebScoket 방식과는 달리 폴링 방식은 매번 일어나는 요청/응답으로 인해 대기 시간이 추가됩니다.







- 클라이언트 코드:


  1. var WScoket = (function (win, doc) {
  2.  
  3.     var ua = window.navigator.userAgent.toLowerCase();
  4.  
  5.     return function (opt) {
  6.  
  7.         function init() {
  8.  
  9.             this.options = {
  10.  
  11.                 socket: null,
  12.  
  13.                 url: '',
  14.                 onopen: function () { ; },
  15.                 onmessage: function () { ; },
  16.                 onclose: function () { ; },
  17.                 onerror: function () { ; }
  18.             };
  19.  
  20.             extend.call(this.options, opt);
  21.  
  22.             window.WebSocket && start.call(this.options);
  23.  
  24.             return this;
  25.         };
  26.  
  27.         init.prototype = {
  28.             send: send
  29.         }
  30.  
  31.         return new init();
  32.     }
  33.  
  34.     function start() {
  35.  
  36.         this.socket = new WebSocket(this.url);
  37.         appendEvents.call(this, this.socket);
  38.     }
  39.  
  40.     function appendEvents(socket) {
  41.  
  42.         var that = this;
  43.  
  44.         bind(socket, 'open', function (e) { that.onopen.call(socket, e); });
  45.         bind(socket, 'message', function (e) { that.onmessage.call(socket, e); });
  46.         bind(socket, 'close', function (e) { that.onclose.call(socket, e); });
  47.         bind(socket, 'error', function (e) { that.onerror.call(socket, e); });
  48.     }
  49.  
  50.     function send(msg) {
  51.         this.options.socket.send(msg);
  52.     }
  53.  
  54.     // 객체 상속 함수
  55.     function extend() {
  56.  
  57.         var target = this
  58.         , opts = []
  59.         , src = null
  60.         , copy = null;
  61.  
  62.         for (var i = 0, length = arguments.length; i < length; i++) {
  63.  
  64.             opts = arguments[i];
  65.  
  66.             for (var n in opts) {
  67.                 src = target[n];
  68.                 copy = opts[n];
  69.  
  70.                 if (src === copy) continue;
  71.                 if (copy) target[n] = copy;
  72.             }
  73.         }
  74.     };
  75.  
  76. })(window, document);
  77.  
  78. function webScoket()
  79. {
  80.     ws = WScoket({
  81.         url: 'ws://echo.websocket.org/',
  82.         onopen: function (e) {
  83.             alert(this);
  84.             alert(e);
  85.         },
  86.         onmessage: function (e) {
  87.             alert(e.data);
  88.         },
  89.         onclose: function (e) {
  90.             alert('close');
  91.         },
  92.         onerror: function (e) {
  93.             alert('error');
  94.         }
  95.     });
  96. };
  97.  
  98. bind(window, 'load', webScoket);
  99.  
  100. // 이벤트 할당
  101. function bind(elem, type, handler, capture) {
  102.     type = typeof type === 'string' && type || '';
  103.     handler = handler || function () { ; };
  104.  
  105.     if (elem.addEventListener) {
  106.         elem.addEventListener(type, handler, capture);
  107.     }
  108.     else if (elem.attachEvent) {
  109.         elem.attachEvent('on' + type, handler);
  110.     }
  111.  
  112.     return elem;
  113. };




- 서버코드:


서버를 구현하는 코드 예제는 상당히 복잡하므로 아래 링크의 테스트 서버로 대체합니다.



WebSocket Test URL: ws://echo.websocket.org/


WebSocket.org 공식 사이트http://www.websocket.org/echo.html





5. 브라우저 지원현황:



브라우저 지원현황:

http://caniuse.com/#feat=websockets






참고 사이트:


- COMET의 소개:

http://hoons.kr/Lecture/LectureMain.aspx?BoardIdx=37384&kind=11&view=0



- .NET을 활용한 COMET의 구현

http://hoons.kr/Lecture/LectureMain.aspx?BoardIdx=37426&kind=11&view=0



- WebSocket Server 구현 방법:


http://stackoverflow.com/questions/9087168/websocket-successfully-handshaking-but-not-sending-receiving-messages-correctly


https://github.com/Olivine-Labs/Alchemy-Websockets


http://www.codeproject.com/Articles/57060/Web-Socket-Server


http://blakeblackshear.wordpress.com/2011/08/25/implementing-a-websocket-handshake-in-c/



prev 1 2 next