Rev. 2.73

HTML 5에 명시된 'localStorage'는 클라이언트 로컬에 데이터를 저장하기 위한 전역 함수입니다. 여기에 저장된 데이터는 장시간 영구적으로 보관할 수 있으며, 오프라인인 경우에도 호출이 가능하다는 특징 때문에 Ajax 응답 결과물 또는 무리한 연산 결과물 등을 캐싱하거나 오프라인 환경을 고려한 데이터베이스 동기화 등 웹 애플리케이션의 성능 향상을 꾀하기 위한 목적으로 사용될 수 있습니다. 부가적으로 서버측 로드는 줄고 트래픽이 절감하는 효과도 발생합니다. 그래서 아래와 같은 스토리지 도우미를 작성했습니다.

/* Local Storage Helper Class */
var Storage = Class.create({
  initialize: function(name, options) {
    this.options = Object.extend({
      clearable: true, // automatically clear data if an error occurred
      delay: 2000, // save data to localStorage after the allotted time
      maximum: 5 // the maximum size in megabytes
    }, options);

    if (typeof localStorage == "undefined" && typeof globalStorage != "undefined")
      window.localStorage = globalStorage[location.hostname];
      this.localStorage = window.localStorage;
    // undefined localStorage if it doesn't already exist
    if (!this.localStorage) this.localStorage = {
        getItem: function(name) {
          this.data = window.name ? window.name.evalJSON() : {};
          return $H(this.data[name] || (this.data[name] = {})).toJSON();
        }, setItem: function(name, data) {
          this.data[name] = data.evalJSON();
          window.name = $H(this.data).toJSON();
        }, removeItem: function(name) {
          delete this.data[name];
          window.name = $H(this.data).toJSON();
        }
      };

    this.name = name || 'unnamed';
    this.data = this.get();
  },
  // get item from localStorage
  get: function() {
    try {
      return (this.localStorage.getItem(this.name) || '{}').toString().evalJSON();
    } catch(e) {
      this.options.clearable && this.set({});
    }
  },
  // set item to localStorage
  set: function(data) {
    if (data) this.data = data;
    this.timer && clearTimeout(this.timer);
    this.timer = setTimeout(function() {
      if (this.size(true) > this.options.maximum * 1048576) return;
      try {
        this.localStorage.setItem(this.name, $H(this.data).toJSON());
      } catch(e) {
        this.options.clearable && this.set({});
      }
    }.bind(this), this.options.delay)
  },
  // remove item
  remove: function(name) {
    this.localStorage.removeItem(name || this.name);
  },
  // remove all items
  clear: function() {
    if (!this.localStorage.length)
      window.name = '{}';
    else
      for (i = 0; i < this.localStorage.length; i++) this.remove(this.localStorage.key(i));
  },
  // size of localStorage
  size: function(bytes) {
    var data = $H(this.data).toJSON().length;
    return bytes ? data : data > 1024 ? (function() {
      data = (data / 1024).round().toString();
      var reg = /(^[+-]?\d+)(\d{3})/;
      while (reg.test(data)) data = data.replace(reg, '$1' + ',' + '$2');
      return data  + 'kb';
    })() : data + 'bytes';
  }
});

/* Usage */
var MyStorage = new Storage('MyStorage'); // Create the helper instance
var MyData = MyStorage.data; // Read data from localStorage(JSON Object)
MyData['firejune'] = { name: 'Junho, Kyung', age: 33 }; // Update a single data
MyStorage.set(MyData); // Write the data to localStorage
MyStorage.size(); // => '49bytes'

위 코드는 이 블로그의 이미지 프로세싱 결과동적인 자바스크립트 호출 결과에 실제로 반영되어 사용되고 있습니다. JSON 자료형으로 입/출력하며, JSON을 문자로 변환 보관하기 위해 json2.js 라이브러리를 필요로 합니다.(브라우저가 Native JSON을 지원하지 않는 경우)합니다. localStorage를 사용할 수 없는 브라우저인 경우에는 단기적으로 자료 보존이 가능한 'window.name' 프로퍼티에 저장하는 트릭을 사용합니다. 그리고 불필요한(비효율적인, 반복적인) 저장 호출들을 무시하기 위해 타이머(기본 2초)가 적용되어 있으며 옵션으로 타이머 시간값을 변경할 수 있습니다. 신기하게도 'localStorage.setItem'메서드로 자료가 입력되는 동시에 하드디스크를 '드르륵' 긁더라구요.

덧1. 문득 떠오른 뻘아이디어: 이렇게 모인 캐시들을 cometd를 이용해서 접속중인 모든 클라이언트에 전송하고 머지해 버리면 어떤 현상이 발생할까요? 이름하여 '미러링 캐시 이펙트'? ㅎㅎㅎ
덧2. 도메인당 최대 자료 저장량이 브라우저에 따라 다르므로 초기화가 자동으로 이루어질 수 있도록 옵션으로 제공 함(Firefox의 최대 저장량 5 MB)
덧3. window.name을 스토리지로 사용하는 경우에도 인스턴스 단위로 구분하여 저장 함
덧4. 해당 인스턴스의 스토리지 사용량을 반환하는 size() 메서드 추가
덧5. window.name을 스토리지로 사용하는 경우 지속적으로 초기화 되던 버그 수정
덧6. 인스턴스당 최대 저장량을 설정할 수 있도록 함(자동으로 초기화하지 않음)
덧7. 구글 크롬에서 발생하는 "localStorage is null" 오류 수정

Comments

마우스 이벤트에 의해 컬러 이미지와 그레이 스케일(흑백) 이미지가 교차되면서 포커싱되는 효과는 시선을 집중시키는 효율적인 방법중 하나입니다. IE의 경우 특정 이미지를 흑백으로 변환하려면 'filter:gray' 스타일 속성을 사용하여 쉽게 해결할 수 있지만 비표준입니다. 비 IE계열 브라우저들은 이에 상응하는 CSS 프로퍼티를 제공하지 않기 때문에 아래와 같은 자바스크립트를 이용하여 이미지를 흑백으로 변환할 수 있습니다.

function grayscale(img) {
  var width = img.getWidth(), height =  img.getHeight();

  // You'll get some string error if you fail to specify the dimensions
  var canvas = new Element("canvas", {width: width, height: height});
  var context = canvas.getContext("2d");
  context.drawImage(img, 0, 0);

  // This function cannot be called if the image is not rom the same domain.
  // You'll get security error if you do.
  var imgData = context.getImageData(0, 0, width, height); 

  // This loop gets every pixels on the image
  for (var x = 0, i; x < width; x++)
    for (var y = 0; y < height; y++)
      imgData.data[(i=x*4+y*4*width)] = imgData.data[i+1] = imgData.data[i+2] =
        (imgData.data[i] + imgData.data[i+1] + imgData.data[i+2]) / 3;

  context.putImageData(imgData, 0, 0, 0, 0, width, height);
  return canvas.toDataURL();
}

// Usages
var img = $('my-image');
img.src = grayscale(img);

img.writeAttribute('src', '/images/smail.gif').observe('load', function() {
  img.stopObserve('load').src = grayscale(img);
});

canvas 요소를 이용한 이미지 프로세싱으로 모든 픽셀을 흑백 데이터로 전환하고 DataURL을 얻는 방법입니다. 위 'grayscale'함수를 사용할 때 세가지 주의해야 할 점이 있습니다. 첫 번째는 이미지의 높이와 너비 값을 얻지 못하는 경우 오류가 발생합니다. 두 번째는 이미지가 완전히 로드된 상태여야합니다. 그렇지 않은 경우 onload 이벤트를 이용하여 호출할 수 있습니다. 그리고 세 번째는 이미지 요소의 'src'속성이 참조하는 경로가 현재 도메인과 일치하지 않는 경우 보안 오류가 발생합니다.

Comments