티스토리 뷰

FrontEnd

IndexedDB 사용시 주의 사항

Jodu 2018. 11. 23. 00:30
반응형

[서론]

이번 개발을 진행하면서 IndexedDB를 사용하게 되었다. 그 이유는 서버로부터 많은 데이터를 받아오게 되는 우리 서비스 환경상 계속해서 서버로 부터 많은 데이터를 계속해서 가져가면 조회 속도에 문제가 발생하여 이를 개선하고자 IndexedDB를 사용하게 되었다. 

이번 포스팅에서는 IndexedDB를 사용하면서 내가 실수 했던 두가지에 대해서 포스팅 할 것이다. 전체적인 소스코드는 별도로 공유하지 않습니다. 

[문제점]

  1. 1. indexedDB에서 데이터를 넣고 조회하는 작업이 모두 비동기라는 점

  2. 2. getAll() 메소드가 많은 데이터를 호출 할 때는 동작하지 않았던 점

위 두가지가 내가 실수 했던 요소 두가지이며 어떻게 해결 했는지에 대해서 공유한다. getAll()이 동작하지 않는 부분에서는 많이 시간을 허비 하였다....

 

[IndexedDB 비동기 동작]

IndexedDB는 모든 동작이 비동기 호출이다. 사용하고자 하는 DB를 오픈하면 오픈 되었다는 onsuccess 메소드 안에서 동작할 코드를 작성하거나 CallBack함수를 실행 하여야 한다.  이 부분은 Document에 명시되어 있고 확인을 한 부분이 였다. 하지만 서버로 부터 데이터를 받아오고 저장을 한 뒤 저장된 데이터로부터 특정 조건의 데이터를 조회하는 소스코드를 작성하는데서 실수를 하였다. 

먼저 데이터를 저장하는 부분의 초기 소스코드를 보자!

 

this.saveData = function(datas) {
        var openDB = openDBFunc();
        openDB.onsuccess = function(event) {
            var db = getIndexedDB(event.target);

            for(var i=0; i<datas.length; i++) {
                var data = datas[i];
                db.store.put(data);
            }

            db.tx.oncomplete = function(event) {
                console.log("complete");
            }
        }
    },

여기서 비동기 호출이므로 onsuccess, opcomplete를 잘 사용하여 소스를 구현하였다. 이렇게만 보면 해당 메소드 자체로는 별 오류가 없는 소스가 되는데 다른 indexedDB 접근 메소드와 함께 쓰이면 문제를 발생 시킬 수 있다. 바로 아래에 해당 소스를 보도록 한다. 

 

this.getAllData = function(callback) {
        var openDB = openDBFunc();
        var getDatas = [];

        openDB.onsuccess = function(event) {
            console.log("DB Open");
            var db = getIndexedDB(event.target);
            //모든 데이터를 가져온다.
            var getData = db.store.getAll();
            getData.onsuccess = function(event) {
                var data = getData.result;
                getDatas.push(data);
            }

            db.tx.oncomplete = function() {
                callback(getDatas);
            }
        }
    }

function errorWork() {
     var receivedData = [...] //서버로 부터 가져온 10만건의 데이터
      saveData();
      getData();
}

errorWork()를 보게 되면 saveData로 서버로 부터 가져온 10만건의 데이터인 receivedData를 저장을 하고 바로 다시 전체 데이터를 조회해 오는 동작을 생각하고 구현한 소스이다. 하지만 이 소스 코드는 다음과 같이 동작하게 된다. 

errorWork()메소드가 동작하면 saveData를 호출 하게 되고 이 호출은 완료까지 꽤 긴 시간이 걸린다. IndexedDB는 특정 트랜잭션이 종료 되어야지만 Commit이 되므로 모든 데이터 저장이 완료 되어야지만 Commit이 된다.  하지만 저장되고 난뒤 동작해야할 getAllData메소드가 saveData가 호출 되자 마자 곧바로 동작하게 되는것을 볼 수 있다. 여기서는 눈에 잘 보이도록 하기 위해 화살표의 간격이 존재 하지만 실제 동작에서는 화살표의 간격이 없는것처럼 동작하게 될 것이다.

그래서 데이터를 가져와야 할 getAllData는 10만건의 데이터가 추가 되기전의 데이터를 가져오거나 혹은 에초에 비어있는 데이터를 가져오게 되는 것이다. 

그렇다면 제대로 동작하도록 만들기 위해서는 어떻게 해야 할까? 해결방법에는 callback 함수를 사용하거나 Promise를 사용하는 방법 두가지가 있는데 여기서는 Promise를 사용해서 설명하도록 한다. 

saveData메소드를 먼저 수정해보도록 하자.

this.saveData = function(datas) {
        return new Promise(function(resolve, reject) {
            var openDB = openDBFunc();
            openDB.onsuccess = function(event) {
                var db = getIndexedDB(event.target);

                for(var i=0; i<datas.length; i++) {
                    var data = datas[i];
                    db.store.put(data);
                }

                db.tx.oncomplete = function(event) {
                    console.log("complete");
                    resolve(true);
                }
            }
        });
}

Promise를 사용해서 onComplete되면 resolve함수로 불리언 값을 던져 준다. 여기서 resolve에 던져주는 값은 필요따라 달라 질 수 있다. Promise에 대해서 잘 모를 경우 여기 를 참고하자. 이렇게 되면 errorWork1 메소드 또한 살짝 수정을 해주어야 한다. 

function work() {
        saveData().then(result => {
            getData();
        });
  }

위 코드는 정상적으로 동작하게 되며 아래의 흐름으로 동작하게 된다. 

이렇게 두가지 호출을 동시에 하면서 각 호출이 서로 상호 관계를 가진다면 callback함수를 사용하던지 Promise를 사용하여야 한다. 그렇지 않으면 예상한 결과와 전혀 다른 결과로 동작하게 되므로 주의하자!!

 

[getAll 메소드의 대용량 데이터 호출 시 동작하지 않는 문제]

100건, 1000건, 10000건을 IndexedDB에서 제공하는 getAll()메소드로 가져오면 정상적으로 잘 동작하게 된다. 하지만 나의 경우 5만건 이상?? 호출 하게 되니까 result값이 undefined으로 반환되는 것과 동시에 onsuccess의 호출이 발생하지 않아서 로직이 아예 동작하지 않는 문제를 접하게 되었다. 

이 문제는 getAll로 한번에 많은 데이터를 가져오면 메모리 관리 측면에서 문제가 발생하므로 막아 둔거 같은 느낌이 들었다. (찾아도 안나오던데... 저의 추측... 입니다.. )

그래서 이 문제는 openCursor로 하나하나 불러와서 처리하는 방식으로 해결 하였다. 

this.getAllData = function(callback) {
        $('#jqxLoader').jqxLoader('open');
        var openDB = openDBFunc();

        openDB.onsuccess = function(event) {
            var datas = [];
            var db = getIndexedDB(event.target);
            var getData = db.store.openCursor();

            getData.onsuccess = function(event) {
                var cursor = event.target.result;
                if(cursor) {
                    datas.push(cursor.value);
                    cursor.continue();
                }
            }

            db.tx.oncomplete = function() {
                callback(gameRecords);
            }
        }
  }

 

반응형

'FrontEnd' 카테고리의 다른 글

Angular Router getting param  (0) 2018.06.28
Angular Deployment  (0) 2018.06.26
URL Shortener 사용법  (0) 2018.04.12
Angular Component간의 연결(Angular Each Component relation)  (0) 2018.03.06
Angular guard 사용법[1]  (0) 2018.02.27
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/04   »
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
글 보관함