티스토리 뷰


* 오랜만에 쓰는 글은 쉬운 내용으로 다시 워밍업을 하려고 한다. 일단 HTML5에 대한 내용들이 이제 대부분의 브라우져에 지원이 되기 시작하고 있어서 슬슬 HTML5에 대해서 부담없이 사용하게 되는 시기가 아닌가 생각한다. 아직 HTML5가 많이 지원 안되는 Internet explorer 8과 9가 10% 가량 남아있기는 한데 마이크로소프트의 강제 업그레이드(감사하게도!!) 덕분에 빠르게 줄어들고 있는 것을 확인할 수 있으니 올해 말 정도에는 이러한 기능들을 마음껏 사용해도 될 것이라 생각한다.



* 이전 편들

2012/10/25 - [HTML5 튜토리얼] HTML5의 등장 배경과 여러 가지 개발 환경 및 현재 현황

2012/11/03 - [HTML5 튜토리얼] 기초 및 기본 구조, 프로그래밍상 기존의 HTML과 바뀐 점

2012/11/05 - [HTML5 튜토리얼] HTML5에 추가된 Element들 그리고 문서의 구조화

2012/11/18 - [HTML5 튜토리얼] Canvas 기초 강좌

2012/11/23 - [HTML5 튜토리얼] Video와 Audio 태그 기본

2012/12/03 - [HTML5 튜토리얼] 새로운 form element 들

(거의 3년반만에.....시간 참 빠르다..)


* 왜 웹스토리지인가?

: 이번에 알아볼 내용은 웹스토리지(Web storage) 내용이다. 웹스토리지라는 이름만 살짝 들어보면 웹에 스토리지가 있는 클라우드와 같은 생각을 할수도 있지만, 웹스토리지라고 하면 HTML5에서 브라우져의 내부 스토리지를 사용할 수 있도록 제공해주는 기능으로 웹에 있는 스토리지가 아니라 브라우져 로컬에 있는 스토리지라고 알고 있으면 좋다. 특히 내부적으로 웹스토리지가 지원해주는 스토리지 방식은 아래와 같이 크게 2가지로 나뉘어져 있다.


- localStorage: 로컬에 origin 별로 지속되는 스토리지

- sessionStorage: 현재 세션 동안만 유지되는 스토리지


: 이러한 스토리지가 주어지게 된 이유는 기존의 쿠키에 대한 제약들을 해소하고 조금 더 다양하고 클라이언트에 많은 기능들을 넣고자 하였기 때문에 HTML5에 새로운 기능인 localStorage와 sessionStorage를 추가한 것이다. 쿠리의 제한점이라고 이야기하는 부분은 아래와 같다.


- 4KB의 데이터 저장 제한

- 쿠키는 매 HTTP 요청에 포함되어 있어 웹이 느려지게 하는 원인이 될 수 있음

- 같은 쿠키는 도메인 내의 모든 페이지에 같이 전달 됨

- HTTP 요청에 암호화 되지 않고 보내기 때문에 보안에 취약함함

- 쿠키는 사용자의 로컬에 텍스트로 저장 되어있어 쉽게 접근, 내용 확인이 가능함


이에 대한 문제점을 보완하기 위하여 용량을 확장하고, 서버로는 전달이 되지 않고 브라우져 로컬에만 저장되며, 보안도 보장할 수 있도록 새로운 표준을 정의한 것이 바로 웹스토리지이다. 이미 대부분의 브라우져는 이러한 스토리지를 지원해주고 있으므로 아직 쿠키를 사용하고 있는 사이트가 있다면 이제는 기술 이전을 해야 할 시기가 왔다고 본다.


* 웹스토리지 용도

: 일단 웹스토리지는 기본적으로 기존의 쿠키를 전부 대체할 수 있다고 볼 수 있다. 쿠키의 가장 대표적인 용도는 바로 "하루 동안 열지 않기" 일 것이다.


<어느 쇼핑몰에 가든 항상 뜨는 그것.. (다음 이미지 업로드 좋네요 모자이크도 되고 크크)>


* (개인적인 의견이지만 이러한 기능은 안 넣으면 좋겠다. 사용자의 입장에서 가장 귀찮은 부분이라고 생각한다. 게다가 정상적으로 동작도 안하는 경우에는 개발자라서 그런지 더 화가난다. 뭐, 아무튼 기획자나 위에서 시킨다면 어쩔 수 없다.)


: 이러한 기능도 있을 것이고 조금 더 유용하게 활용하려면 개인화 기능을 웹스토리지로 관리하면 아주 편리하게 활용할 수 있다.



<이러한 기능에 대한 설정 저장을 로컬에다가도 할 수 있다.>



: 특히 읽은 글에 대한 기록과 같이 서버와 조금 과도하게 통신이 필요한 경우 로컬스토리지에다가 쉽게 저장하여 관리할 수 있어서 매우 편리하다. 이외에도 간단한 이미지나 텍스트 파일 등과 같은 파일 정보를 스트링으로 변환하여 저장하는 것도 가능하고, 글쓰기를 하다가 저장하지 못했는데 인터넷이 끊기에 되는 경우 로컬에 자동 저장 등의 기능으로 아주 편하게 사용할 수 있다.


: 이러한 기능을 사용하면 이전의 쿠키만으로 제공 가능하던 기능들보다 더 다양하면서 더 좋고 더 편리하게(특히 개발자 입장에서) 제공해줄 수 있어서 쿠키를 걷어내고 사용하기에 아주 좋은 기능이라고 생각한다. 그렇다면 이러한 기능을 사용하기 위한 예제를 먼저 살펴보자.


* Storage API 예제

: 개발자들은 이론으로 말하기 보다는 실제 예제를 보기를 선호하므로 먼저 샘플부터 펼쳐놓고 표준이든 함께 한번 살펴보자.


<html>
<body>
    <input type="text" id="text" />
    <div id="log">
</body>
<script>
(function () {
    var text = localStorage.getItem("inputLog") || "",
        divLog = document.getElementById("log"),
        inputText = document.getElementById("text");
    if (text) {
        divLog.innerHTML = text;
    }
    inputText.addEventListener("change", function () {
        text += inputText.value + "
"; divLog.innerHTML = text; localStorage.setItem("inputLog", text); inputText.value = ""; }); }()); </script> </html>


: 위의 예의 실행 결과는 아래와 같다.



: 간단하게 소스를 살펴보면 하나의 <input> 박스가 있고, 또 하나의 <div> 태그가 있다. <input> 태그에 change 이벤트 핸들러를 할당하여 값이 입력되면 그것을 <div>에 설정하고 localStorage에 저장하고 있다. 그리고 페이지가 처음에 로드 될 때 localStorage에 이전에 저장된 내용이 있으면 불러와서 <div> 태그에 내용을 붙여넣는다. 위의 예만 보면 아주 쉽다. localStorage에 저장하는 것은 setItem, 가져오는 것은 getItem으로 인자는 키 값을 의미한다. 입력되는 내용이 계속 localStorage에 저장되기 때문에 다시 웹페이지를 닫았다가 열어도 그 내용을 그대로 다시 설정하는 것이 가능하다. 이렇게 localStorage의 사용은 아주 쉽다. 웹스토리지의 사용법은 여기까지만 봐도 충분히 응용할 수 있으리라 생각한다. sessionStorage의 사용법도 그냥 똑같다. 웹스토리지 사용법. 끝. 알아서 응용하면 된다.



* 조금 더 살펴보고 싶은 이들에게.. 표준!

: 웹스토리지의 경우 과연 보안에 100% 안전할까 생각해보게 된다. 사실 그냥 텍스트로 저장하면 브라우져 내에서의 보안상 이점은 큰 이득은 없을 수 있다.




: 위와 같이 실행했던 웹페이지에서 브라우져 콘솔을 열고 localStorage의 내용을 살펴보니 텍스트가 전부다 나오게 되는 것을 확인할 수 있다. 따라서, 클라이언트-서버의 구조에서 '당연한' 이야기이지만, 웹스토리지에는 일단 보안이 중요한 내용은 웬만하면 넣지 않는게 이롭다. 혹시 넣어야 한다면 애초에 서버에서부터 암호화해서 로컬에 저장하는 것이 그나마 낫지만, 보안이 중요한 데이터를 클라이언트로 보내야한다는 것 자체가 보안이 취약해질 수 있는 리스크를 안고 가는 것이라 애초에 설계가 잘못 되었다고도 볼 수 있기도 하다.


: 아무튼 위의 스크린샷에서는 브라우져의 개발자 콘솔로 직접 접근이 가능하다는 것도 보여주고 싶었지만, __proto__ 속성으로 어떠한 prototype이 설정되었는지 살펴보면 바로 Storage가 프로토타입으로 설정되어있다. 이 부분을 다시 한번 개발자 콘솔에서 확인해보면 아래와 같다. 지금 한번 개발자 콘솔을 열어서 보는 것도 가능할 것이다.




: Storage 는 함수로 내장 모듈로 제공해주고 있으며(크롬 기준..), Storage.prototype은 위와 같이 정의 되어있다. localStorage와 sessionStorage는 모두 위의 동일한 prototype을 사용하고 있으며, 이 함수들이 웹스토리지 API의 기본 인터페이스가 된다. 이에 대한 내용은 표준으로도 정의되어있다. 아래의 URL에서도 관련 표준을 확인할 수 있다.


https://www.w3.org/TR/2016/REC-webstorage-20160419/


: 표준에서는 아래와 같이 정의되어있다.




: 위의 각각을 보면 아래와 같이 localStorage와 sesstionStorage가 API를 제공해주고 있다고 볼 수 있다.


- length: 읽기 전용 현재 Storage의 길이

- key(int): 해당 인덱스 위치에 있는 문자열 키 값을 리턴

- getItem(key): 해당 키 값에 해당하는 정보(문자열)을 리턴

- setItem(key, value): 해당 키 값에 대하여 해당 값을 설정

- removeItem(key): 해당 키 값의 값을 삭제

- clear(): Storage 전체 내용을 삭제


: 위의 표준을 보면 재미있는 것이 getItem은 getter로 설정이 되어있고, setItem은 setter로 설정이 되어있다. 위의 표준에 따라서 getItem과 setItem 만으로 설정할 필요 없이 바로 localStorage와 sessionStorage가 마치 하나의 객체인 것 처럼 사용해서 데이터에 접근할 수 있다. (물론, 표준대로 브라우져가 지원해주는 경우..) 따라서 앞서 나온 소스를 아래와 같이 고쳐도 동일하게 동작한다. 그리고 removeItem을 deleter로 delete 키워드로도 해당 내용을 삭제할수도 있다는 것을 나타낸다. 처음의 text를 가져오는 부분과 설정하는 부분이 조금 더 간결하게 바뀐 것을 확인할 수 있다.


<html>
<body>
    <input type="text" id="text" />
    <div id="log">
</body>
<script>
(function () {
    var text = localStorage["inputLog"] || "",
        divLog = document.getElementById("log"),
        inputText = document.getElementById("text");
    if (text) {
        divLog.innerHTML = text;
    }
    inputText.addEventListener("change", function () {
        text += inputText.value + "
"; divLog.innerHTML = text; localStorage["inputLog"] = text; inputText.value = ""; }); }()); </script> </html>



: 표준의 조금 다른 항목을 보면 재미있는 것이 바로 'storage' 이벤트이다.  



* storage 이벤트

표준에서는 "localStorage"와 "sessionStorage"의 동작이 명시되어있는대로 이벤트를 발생시킨다고 하는데, 이것이 명시되어있는 부분을 보면 아래와 같다.




: 해석해보면 setItem, removeItem, clear 함수가 호출이 될 때 정상 처리 되면, 다른 모든 윈도우에 같은 locatlStorage를 참조하고 있는 Document에 이벤트를 발생시킨다고 되어있다. 즉, localStorage를 변경하면 현재에 있는 웹페이지에서는 알림이 가지 않고, 다른 탭이나 새로운 창에서 같은 localStorage를 사용하고 있으면 해당 document에 이벤트를 발생시킨다고 되어있다. 그러면 이벤트의 설정되는 정보를 살펴보면 아래와 같이 명시되어있다.




: 이벤트는 전부다 null로 초기화 되며, 아래와 같이 각 칼럼의 용도가 있다.


- key: 값이 변경된 키 값

- oldValue: 이전 값

- newValue: 새로운 값

- url: 변경이 일어난 URL

- storageArea: 변경이 일어난 Storage 객체


: 위와 같이 변경에 대한 거의 모든 정보들이 들어오기 때문에 이벤트가 일어나게 되면 "key" 값에 따라 변경을 감지하여 환경설정이나 페이지의 정보를 업데이트 해주면 된다. 이에 대한 간단한 테스트를 로컬해서 해보면 아래와 같이 가능하다. 


<html>
<body>
    <input type="text" id="text" />
    <button id="clear">Clear</button>
    <div id="log"></div>
</body>
<script>
(function () {
    var text = localStorage.getItem("inputLog") || "",
        divLog = document.getElementById("log"),
        inputText = document.getElementById("text"),
        buttonClear = document.getElementById("clear");
    if (text) {
        divLog.innerHTML = text;
    }
    inputText.addEventListener("change", function () {
        text += inputText.value + "<br />";
        divLog.innerHTML = text;
        localStorage.setItem("inputLog", text);
        inputText.value = "";
    });
    buttonClear.addEventListener("click", function () {
        localStorage.clear();
    })
    window.onstorage = function (e) {
        console.log("Storage event has occured");
        console.log(e);
    };
}());
</script>
</html>

: 위와 같이 실행한 다음, 크롬의 두 탭으로 열면, 서로 데이터를 변경하면 이벤트가 발생해서 로그를 남겨야 할 것 같은데 이벤트가 일어나지 않는다. 로컬에 대한 쓰기 권한이 없어서인지 크롬에서는 아래의 추가 파라미터를 주고 열어야 storage 이벤트를 주고 받는 것이 가능하다. 아래는 맥환경에서 추가 파라미터로 실행하는 방법이다.


open /Applications/Google\ Chrome.app/ --args --allow-file-access-from-files







: 이렇게 실행해서 열린 크롬은 두 탭간에 storage 이벤트를 주고 받는 것이 가능하여 실행결과 아래와 같이 이벤트를 주고 받으며, 표준에 나와있는 이벤트 객체의 속성들도 나타나는 것을 확인할 수 있다. 아래는 setItem의 결과이다.


: 위와 같이 setItem을 하게 되는 경우 oldValue는 null이고 newValue는 새로 설정된 값이 들어온 것을 확인할 수 있고, 키 값은 소스에서처럼 inputLog로 변경된 값을 확인할 수 있다. 아래는 clear가 발생했을 때의 이벤트로 키 값과 newValue, oldValue 전부다 null로 들어온 것을 확인할 수 있으므로, 이것으로 clear인지, setItem인지 등을 구분하여 이벤트 처리를 하면 된다.



: 이렇게 storage 이벤트는 같은 웹스토리지를 사용하는 브라우져에서 서로 다른 탭이나 다른 창이 열려있는 경우 서로 변경되었다는 것을 알려주어, 로컬 스토리지의 변경 점을 각 페이지에 적용하거나 또 다른 활용 방안과 서로간에 간단하게 통신할수도 있는 방법으로 활용이 가능하다.



* 기타 표준에서 다루는 내용들..

: 웹스토리지에 대해서 표준에서 더 다루고 있는 내용은 아래와 같다.


- 디스크 용량

  * 브라우져는 악의 적으로 용량을 소모 시키는 사람들이 있을 수 있으므로 스토리지의 최대 용량을 제한해야 하며 한 사이트당 5메가를 권장한다.

  * 사용자는 도메인이 서로 연관된 사이트들이 전체 용량 제한을 피해가기 위해 동작하는 것을 막아야 한다. 예를 들면, a1.example.com과 a2.example.com 등의 별도 도메인을 통해서 example.com의 전체 용량 제한을 피해가는 것을 막아야 한다. 

  * 브라우져는 웹페이지에 대해 추가적인 스토리지를 할당할 수 있는 기능을 제공해야 한다.

  * 브라우져는 각 도메인에서 얼마나 용량을 사용하는지 볼 수 있어야 한다.

  * 용량 산정 기준은 압축이 되지 않은 기준으로 한다.


- 개인정보

  * 제3의 사이트가 iframe 등으로 다른 도메인에 대한 스토리지를 접근하고자 한다면 이를 막아주어야 한다.
  * 브라우져는 사용자의 설정에 의하여 자동으로 저장된 데이터를 삭제하는 기능이 있어야 한다. 장기간 데이터를 보관하는데 있어서 사용자가 이 부분을 제대로 이해하지 못하면 항상 데이터 삭제에 대한 위험성이 있다.
  * 쿠키처럼 데이터를 삭제하는 기능을 제공하여 사용자가 모든 데이터를 삭제할 수 있는 기능을 제공해야 한다.
  * 보안으로 스토리지 접근을 차단하고 사용자가 선택적으로 이를 허용할 수 있는 기능을 제공해야 한다.
  * 제3의 사이트가 스토리지에 접근하고자 한다면 이를 조회하고 블랙리스트로 차단할 수 있어야 한다.
  * 커뮤니티에서 이러한 사이트들을 공유할 수 있는 기능을 제공해야 한다.
  * 브라우져는 지속되어야 하는 데이터들에 대해서는 민감하게 보관하고 삭제할 때에는 알려주어야 한다.

- 보안

  * DNS spoofing 공격으로 인하여 현재 도메인에 대해서 보장을 할 수 없기 때문에, 웹페이지들은 TLS를 사용할 것을 권장한다.
  * 같은 도메인에 서로 다른 디렉토리를 사용하는 경우 스토리지를 나눌 수가 없으므로 이러한 특징의 공유 서버에서 서비스를 제공하는 경우 사용을 피할 것을 권장한다. 다른 사이트에서 데이터를 읽어가고 덮어씌우는 것은 아주 쉽게 일어날 수 있다.
  * 제3의 사이트가 스토리지를 접근하고자 하는 것은 사용자 보안을 위해서 막아야 하고, 스토리지를 origin별로 나누는 것에 대해서 철저하게 지켜야 한다. 


: 위의 내용들은 브라우져에서 지켜야 하는 내용들로 개발자의 입장에서는 크게 고민할 필요는 없지만, 보안 중에서 "같은 도메인의 서로 다른 디렉토리"의 공유서버 환경에서 서비스를 제공하는 경우에는 절대로 민감한 데이터를 스토리지에 저장해서는 안 될 것이라고 이미 표준에서 부터 권장하고 있으므로 참고하자. 이해하기 쉽게 예를 들면, http://blog.naver.com/sheepsi 에서 사용하고 있는 localStorage를 사용하는 사이트와 http://blog.naver.com/unikys에서 사용하고 있는 localStorage는 결국 blog.naver.com의 도메인이 같기 때문에 스토리지가 공유된다는 내용이다.


* 그래도 해결되지 않은 문제점?!

: 웹스토리지는 쿠키의 다양한 단점들을 극복하기 위하여 나왔기 때문에 쿠키가 있었던 거의 모든 중요한 단점들은 해결이 되었다. 하지만 쿠키에서도 있었던 몇 가지 문제들을 웹스토리지에서도 그대로 가지고 있기 때문에 주의하면 좋은 부분들도 있다. 바로 첫번째는 브라우져간 스토리지 공유가 되지 않는다는 점이다. 이 부분은 서로 다른 컴퓨터라는 문제 뿐만 아니라 같은 컴퓨터 안의 다른 브라우져에서라도 스토리지가 공유되지 않고 브라우져들이 독자적으로 스토리지를 관리하기 때문에 항상 같은 내용을 보유하고 있어야하는 경우에는 스토리지를 사용하는 것이 아니라 서버에서 해당 내용을 관리하는 것이 좋다. 그리고 또 다른 문제(?)는 아니고 고려해야 할 점은 바로 최근 브라우져들이 지원하고 있는 "Private 모드"이다. Private모드에서는 스토리지가 동작은 하나 일반적으로 Private모드의 세션이 끝나고 나면 관련 스토리지는 전부다 지워지게 된다. 따라서 사용자가 변경한 내용에 대해서 Private 모드임에도 계속 유지해야 하는 내용 역시 서버에서 관리하는 것이 맞다.



* 자꾸 고려해야한다고 하는데..

: 위에서부터 자꾸 '고려해야 한다' '고민해보면 좋다' '조심해야 한다' 등등 말을 늘어놓고 있는데, 그러면 언제 쓰라는 말이냐?!고 묻는다면, "중요한 기능"에는 사용하지 말라고 권하고 싶다. 비즈니스 로직에 핵심적인 기능을 웹스토리지에 넣는 것은 브라우져 환경과 모바일 환경, 그리고 사용자의 개인적인 웹 브라우징 성향에 따라서도 웹스토리지는 항상 잘 동작하지 않을수도 있다. 그러면 언제 쓰는 것이 가장 좋은가 고민해보면, 간단한 기능들인데 없어도 되고 있으면 좋은 그러한 기능들을 웹스토리지를 사용해서 구현하면 좋다. 가장 대표적인 예는 아래와 같을 것이다.


- '오늘 하루 열지 않기'

- sesstionStorage를 활용해서 사용자가 '입력폼'을 입력하다가 페이지에서 벗어난 경우 백업/복구

- 글쓰기를 하다가 사용자가 창을 벗어난 경우 관련 작성하던 내용 백업/복구용

- 웹서버에 필수적으로 접근해야 하는 캐쉬용(캐쉬로 먼저 서비스 제공, 차후에 업데이트)

- 웹페이지의 개인화 설정들에 대한 저장과 제공(캐쉬로 활용)

- 현재 읽은 글의 히스토리 저장(카운팅, 읽은글 표시 등으로 활용)

- Canvas나 이미지에 대한 임시 저장 기능(base64로 변환)

- 웹페이지간 정보 전달(웹서버를 경유하지 않고 정보 로컬에 유지)

- 중요 CSS 저장용(참조: https://speakerdeck.com/patrickhamann/css-and-the-critical-path-cssconfeu-september-2014)


: 위의 경우들을 보면 굳이 동작하지 않아도 치명적이지 않은 기능들이다. 그리고 굳이 다른 브라우져/컴퓨터와 공유되지 않아도 문제 없거나 웹서버에서 쉽게 불러들일 수 있는 내용들이다. 이렇게  localStorage는 나름 주목을 할만한 기능이라고 생각할수도 있지만, 로컬이라는 한계 때문에 결국은 쿠키의 확장형 기능들을 제공하는 것이 적합하다고 생각한다. 물론 위의 예 중 마지막인 웹페이지간 정보 전달을 하는 경우 비즈니스 로직으로 활용하는 것이 아주 나쁜것만은 아니기는 하지만 일단 sessionStorage로 사용할 것을 권장하고 전체적인 페이지의 흐름에 대해서 면밀이 검토해보는 것이 좋다고 생각한다.



* 다음편

2017/01/09 - [HTML5 튜토리얼] navigator.geolocation 위치 정보 수집 API


끝.


* 다음편은 아마도 IndexDB과 Web SQL에 대해서 사용법과 표준에 대해서 살펴볼 예정이다.








공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/03   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함