용량제약의 문제
localStorage는 도메인별 용량 제한이 있습니다. 보통 5메가 정도를 부여받는데 이는 브라우저별로 매우 상이합니다. 특히 localStorage, sessionStorage, globalStorage 의 정책도 전부 제각각입니다. 도메인에 할당된 용량은 객체에서 직접 지원하는 메서드를 통해 확인할 수 있습니다.
console.log( localStorage.remainingSpace );
헌데 전혀 다른 문제가 존재합니다. 도메인 전체에 할당된 용량제약만 있는 것이 아니라 개별 키에도 용량제약이 존재한다는 사실입니다. 정확한 용량제약의 기준도 없고 이를 확인할 수 있는 메서드도 없습니다. 그저 setItem을 할 때 예외를 토하고 죽어버립니다.
var value = "상당히 큰 text"; localStorage.setItem( 'test', value );
위의 소스에서 자바스크립트가 멈춰버리고 전체가 작동중지됩니다. 브라우저마다 하나의 키에 넣을 수 있는 용량에 대한 정확한 제약조건을 알 수는 없는 노릇입니다.
예외를 이용하여 쪼개서 저장하기
일단은 전체 스크립트가 죽어버리므로 setItem은 함부로 할 수 없습니다.
try{ localStorage.setItem( 'test', value ); }catch(e){ }
이 구조를 바탕으로 실패하면(catch에 들어오면) 용량을 점점 쪼개본다 라는 전략을 사용해보죠. 알고리즘은 다음과 같이 간단히 구성합니다.
- 무한루프를 걸고
- try가 무사히 처리되면 break로 루프를 빠져나온다.
- catch에 걸릴때마다 점점 더 잘게 나눠서 저장한다.
- 나눠서 저장한 경우는 해당 키에 특수한 형태로 쪼개져있다는 정보를 남겨준다.
뭐 간단합니다. 기본골격부터 만들죠
//무한루프 while(1){ try{ if( 쪼개진 적이 없다면 ){ localStorage.setItem( 'test', value ); }else{ //해당키에는 특수한 값을 넣고 쪼개진 데이터를 별도로 나눠서 저장함 } break; //루프를 빠져나감 }catch(e){ //더 쪼개는 장치 } }
어느정도 골격이 잡혔으니 쪼개는 전략을 고민해보면 가장 단순하게는 절반으로 나눠서 넣어보고 안되면 1/3으로 나누고 안되면 1/4로 나누는 식으로 짜볼 수 있습니다. 분모를 점점 증가시켜가면 되는거죠.
//분모설정 var deno = 0; //최초는 쪼개지 않음 while(1){ try{ if( !deno ){//쪼개진적이 없음 localStorage.setItem( 'test', value ); }else{ //특수한 값으로 저장 '--분모' 형태 localStorage.setItem( 'test', '--' + deno ); //분모를 통해 한번에 저장할 크기를 얻는다 var partSize = Math.ceil( value / deno ); //분모만큼 루프를 돌면서 분할하여 저장 for( var i = 0 ; i < deno ; i++ ){ localStorage.setItem( 'test::' + i, value.substr( i * partSize, partSize ); } break; }catch(e){ //실패시마다 분모를 증가시킨다 deno++; } }
위 코드는 결국 try catch가 성공할때까지 루프를 돌며 점점 작게 쪼개서 넣게 됩니다. 실제 사이즈가 커서 쪼개지면 다음과 같은 형태로 저장될 것입니다.
'test' = '--3' 'test::0' = '....' 'test::1' = '....' 'test::2' = '....'
즉 쪼개져있는 경우는 해당 키를 얻으면 –로 시작하는 값이므로 이를 바탕으로 값을 모아 조합하는 힌트가 됩니다.
쪼개진 값을 다시 조합하기
위의 힌트를 이용해 재조립만 하면 되므로 사실 간단합니다.
var data = localStorage.getItem('test'); if( data.substr(0,2) == '--' ){ //특수한 형태다! var temp = '', i, j; for( i = 0, j = parseInt(data.substr(2)) ; i < j ; i++ ){ //쪼개진 조각을 한데 모아서 합체! temp += localStorage.getItem('test::'+i); } data = temp; } console.log( data );
setItem할때 사용한 프로토콜을 이용해 복원코드를 넣으면 간단히 해결됩니다. 마찬가지로 삭제도 간단히 처리됩니다.
var data = localStorage.getItem('test'); if( data.substr(0,2) == '--' ){ //특수한 형태다! for( var i = 0, j = parseInt(data.substr(2)) ; i < j ; i++ ){ //쪼개진 조각을 전부 삭제! localStorage.removeItem('test::'+i); } } //최종적으로 키도 삭제 localStorage.removeItem('test');
결론
평화롭고 짧막한 로컬스토리지 사용이 키별 용량제약으로 순식간에 지옥이 되고 맙니다. IE11의 경우 하나의 키에 들어갈 수 있는 용량이 얼마 안되기 때문에 상당히 괴롭습니다.
부디 고통 받는 영혼들이 계시면 작은 도움이 될 수 있길 바랍니다. 함수형태로 간단히 정리된 코드는 다음과 같습니다.
function setItem( k, v ){ var deno = 0, i, j; while(1){ try{ if( !deno ) localStorage.setItem( k, v ); else{ localStorage.setItem( k, '--' + deno ); j = Math.ceil( v / deno ); for( i = 0 ; i < deno ; i++ ) localStorage.setItem( k + '::' + i, v.substr( i * j, j ); } break; }catch(e){ deno++; } } } function getItem(k){ var data = localStorage.getItem(k), temp, i, j; if( data.substr(0,2) == '--' ){ for( temp = '', i = 0, j = parseInt(data.substr(2)) ; i < j ; i++ ) temp += localStorage.getItem( k + '::' + i ); data = temp; } return data; } function removeItem(k){ var data = localStorage.getItem(k), i, j; if( data.substr(0,2) == '--' ){ for( i = 0, j = parseInt(data.substr(2)) ; i < j ; i++ ) localStorage.removeItem( k + '::' + i ); } localStorage.removeItem(k); }
recent comment