idb library 공식 문서를 토대로, idb library 특징과 저장할 수 있는 데이터 갯수를 제한하는 리팩토링 과정을 정리하였습니다.
🙋♀️ IndexedDB API에 대해 궁금하신 분들은 Indexed API 분석 정리 게시물을 먼저 참고해주시면 감사하겠습니다.
IndexedDB에 데이터를 누적 저장한 후, 특정 로직을 통해 최종 데이터를 보여주는 기능에서 초반에는 3~4초
후반에는 7~8초의 로딩 시간이 걸리면서 UI 동작을 할 수 없는 문제가 발생하였다.
해당 문제는 IndexedDB에 저장하는 데이터가 증가함에 따라 로직 처리 시간이 오래 걸려 싱글쓰레드에서 UI blocking 현상으로 인해 발생된 것이었다.
이는 유저 경험을 저하시키고, 시간이 지날수록 해당 로직 처리 시간이 길어진다는 단점이 생겨 최근 데이터를 기준으로 특정 갯수까지만 저장하도록 limit을 걸기로 하였다.
해결 방법을 구체화하기 전, 이번에 사용한 idb라이브러리와 IndexedDB API에 대해 잠깐 다루어본다.
IDBRequest
object를 리턴하는 메서드들은 idb에서 결과에 대한 Promise를 반환한다.await
를 걸지 않는다. 걸게 되면, transaction이 닫혀 이후 작업이 불가하다.store.put
에서 오류가 발생하게 되는 것이다. const tx = db.transaction('keyval', 'readwrite');
const store = tx.objectStore('keyval');
const val = (await store.get('counter')) || 0;
// This is where things go wrong:
const newVal = await fetch('/increment?val=' + val);
// And this throws an error:
await store.put(newVal, 'counter');
await tx.done;
현재 IndexedDB API를 반영한 idb 라이브러리를 사용하여 DB를 구축하였기 때문에, idb 공식 자료를 기반으로 리팩토링을 진행하였다.
주요 핵심 기능은 cursor
이라는 idb method이다.
indexedDB는 객체 저장소를 순회하기 위해서 for문을 바로 사용해서는 안되고, cursor로 순회하고자 하는 객체 저장소의 cursor를 얻은 후 순회해야한다.
db.transaction
메서드로, 연결된 DB에서 문제가 발생하는 store를 기준으로, 해당 스토어의 모든 키들을 꺼내온다getAllKeys
메서드를 사용했다.// 1번째 방식
.
.
// db 연결 완료
.
.
// 기본적인 스토어 얻는 방법
const tx = db.transaction("store name", "readwrite");
const store = tx.objectStore("store name");
// key로 데이터에 접근하여 삭제하기 위해, key 배열 얻어내기
const keys = await db.getAllKeys("store name");
.
.
참고로 1번과 2번의 코드는 같다. 앞서 차이점에서 언급했듯이, 1번 코드에서는 transaction의 시작과 끝 사이에서는 await를 걸지 않았다는 것을 볼 수 있다.
나는 코드 가독성을 위해 위의 방식대로 진행하였다.
// 2번째 방식
.
.
// db 연결 완료
.
.
const tx = db.transaction("storeName", "readwrite").objectStore("storeName");
const keys = await db.getAllKeys("storeName");
.
.
openCurosr()
메서드로 cursor를 얻어, 해당 cursor가 있을 경우 삭제할 갯수만큼 for문으로 순회한다.cursor.continue()
로 cursor를 갱신한다..
.
let cursor = await db.transaction("storeName").store.openCursor();
if (cursor) {
for (let i = 0; i < "순회횟수"; i++) {
db.delete("storeName", keys[i])
// 예외처리
if (cursor === null) {
return;
}
// continue로 다음 커서를 요청한다.
cursor = await cursor.continue();
}
}
.
.
이외에도 get
, getKey
, getAll
, getAllKeys
, count
, put
, add
, delete
, 그리고 clear
메서드로 필요한 작업을 진행할 수 있다.
참고 자료