개발 기술/개발 이야기

indexedDB에 대해 알아보자!

by GicoMomg (Lux) 2022. 1. 24.

🤔 indexedDB는 무엇이며, 어떻게 사용하는 걸까? 이번 시간에는 indexedDB정의와 사용법에 대해 알아보았다! (마지막에는 vue에서는 indexedDB를 어떻게 사용하는지 링크도 공유해두었다 참고해보자!)

1. indexedDB?

1) indexedDB는 무엇일까?

(1) indexedDB의 정의

  • 우리가 데이터를 저장할 때, 클라이언트 스토리지(브라우저 제공 db)를 이용할 수 있다.
  • 클라이언트 스토리지에는 localStorage, cookies, indexedDB가 존재한다.

❓ 그런데 localStorage 대신 indexedDB를 쓰는 이유가 있을까?

  • localStoage의 경우
    : 적은 양의 데이터를 저장할 때 유용하다.
    : 동기적으로 작동하며, 오직 문자열 타입의 키&값만을 사용할 수 있다.
    : 그렇기 때문에 JSON.stringify, JSON.parse와 같은 함수가 꼭 필요하다.
  • indexedDB의 경우
    : 그에 반해 indexedDB는 많은 양의 구조화된 데이터를 저장할 때 유용하다.
    : localStoage와 달리, JS가 인식할 수 있는 자료형과 객체를 저장할 수 있다.
    : 키, 값 형태로 데이터가 저장되며 문자열 타입이 아니어도 된다.
    : 특이한 점은 비동기적으로 작동한다는 점이다.

(2) indexedDB 패턴

❓ indexedDB는 localStoage보다 절차가 조금 복잡한데, 기본 패턴은 아래와 같다.

  • 데이터베이스 열기
  • 데이터베이스에 객체 저장소(Object store) 생성하기
  • 트랜젝션(Transaction) 시작하기 (데이터 읽기, 쓰기 제거 등 데이터베이스 작업 요청)
  • 이벤트 리스너를 사용하여 요청이 완료될때까지 기다리기
  • 요청 결과를 가지고 어떤 동작하기






2. indexedDB 사용해보기

1) 데이터베이스 열기

(1) open 함수로 dataBase 열기

  • 브라우저에서 여러 개의 데이터베이스를 만들 수 있다.
  • 데이터베이스는 버전 정보를 가지고 있으며, 여러 개의 ObjectStore(객체 저장소)를 가질 수 있다.
  • 단, 데이터베이스 수정시 버전을 수정해줘야 한다!
  • open 명령어를 사용해 데이터베이스를 열도록 요청할 수 있다.
window.indexedDB.open(db_name, version)

(2) 예시 코드

  • 아래 코드는 데이터베이스를 여는 과정이다.
// 1. indexedDB 객체 가져오기
const idxedDB = window.indexedDB;

// 2. 브라우저에서 지원하는지 체크하기
if (!idxedDB) window.alert('해당 브라우저에서는 indexedDB를 지원하지 않습니다.')
else {
  let db;
  const request = idxedDB.open('SampleDB');   // 3. SampleDB(db) 열기
  request.onerror =(e)=> alert('failed');
  request.onsuccess =(e)=> db = request.result;

}



2) database에 ObjectStore 추가하기

(1) ObjectStore 생성하기

  • ObjectStore는 데이터를 담는 공간이다.
  • 여러 개의 레코드(key-value)를 가진다.
  • ObjectStore의 이름은 고유해야 한다.
  • 각 객체에 유일성을 부여하기 위해 keyPath를 정의해야 한다.
  • createObjectStore를 사용해 특정 DBObjectStore를 추가할 수 있다.
  • 단, createObjectStoreonupgradeneeded 이벤트와 함께 써야 한다.
IDBRequest.createObjectStore('store_name', {keyPath: 'id'})

(2) onupgradeneeded?

  • onupgradeneeded는 아래와 같은 상황에 발생하는 이벤트이다.
  이벤트 발생 상황
  새로운 데이터 베이스 만들 때
  기존 데이터베이스의 버전을 높일 때(db를 열었는데(open), 이전보다 높은 버전을 지정하는 경우)
  • 해당 이벤트가 성공하며 onsuccess 핸들러가 트리거 된다.
  • indexedDB.open(name, version).onupgradeneeded와 같은 형식은 지원하지 않는다.

(3) 예시 코드

  • 아래 코드는 SampleDB를 열고, 해당 dbname이라는 이름의 ObjectStore를 추가한 모습이다.
// 1. indexedDB 객체 가져오기
const idxedDB = window.indexedDB;

// 2. 브라우저에서 지원하는지 체크하기
if (!idxedDB) window.alert('해당 브라우저에서는 indexedDB를 지원하지 않습니다.')
else {
  let db;
  const request = idxedDB.open('SampleDB');   // 3. SampleDB(db) 열기

  request.onupgradeneeded =(e)=> { 
    db = e.target.result;
    db.createObjectStore('name', {keyPath: 'id'}); // 4. name저장소 만들고, key는 id로 지정
    request.onerror =(e)=> alert('failed');
    request.onsuccess =(e)=> db = request.result;  // 5. 성공시 db에 result를 저장

  }

}



3) 트랜잭션 시작하기

  • indexedDB.open 성공시(onsuccess), transaction을 사용해, 저장소에 데이터를 입력할 수 있다.
  • 단, 새 데이터베이스에서 작업을 하기전에 트랜잭션을 시작해야 한다!

(1) 트랜잭션은 무엇일까?

❓ 트랜잭션이 뭐길래, 이걸 먼저 시작해야할까?

  • 트랜잭션은 db의 상태르 변화시키기 위해 수행하는 작업 단위를 말한다.
  • 트랜잭션데이터베이스 객체 단위로 작동하며, 그렇기 때문에 사용할 객체 저장소를 지정해줘야 한다.
  • 우리는 트랜잭션을 통해, 객체 저장소에 접근하거나 데이터 요청을 할 수 있다.
  • 트랜잭션은 다음 3가지 모드가 있다.(readonly, readwrite, versionchange)

(2) 트랜잭션 3가지 모드

versionchange 모드

  • 아래와 같은 상황은 versionchange에서 처리해줘야한다.
  상황
  객체 저장소를 만들 때
  객체 저장소에서 인덱스를 만들거나 삭제할 때
  데이터베이스의 스키마나 구조를 변경할 때
  • 해당 트랙잭션은 indexedDB.open시 버전을 지정한 경우에 시작해야 한다.
  • 단, 일부 webKit 브라우저의 open()는 데이터베이스의 이름(name) 하나만 인자로 받는다.
  • 따라서 별도로 setVersion()를 호출해야 한다.

readonly 모드

  • 객체 저장소의 레코드를 읽을 때, readonly에서 처리해줘야한다.
  • IDBDatabase.transaction으로 해당 모드를 열 수 있다.

readwrite 모드

  • 아래와 같은 상황은 readwrite에서 처리해줘야한다.
  상황
  객체 저장소의 레코드를 읽을 때
  존재하는 객체 저장소에 변경점을 기록할 때
  데이터베이스의 스키마나 구조를 변경할 때
  • IDBDatabase.transaction으로 해당 모드를 열 수 있다.

(3) transaction() 사용하기

  • 우리는 transaction()을 사용해 특정 객체 저장소에 접근할 수 있다.
  • 모드가 지정되지 않는다면 기본적으로 트랜잭션은 readonly 모드로 열린다.
  • 해당 메소드는 객체 저장소에 접근할 수 있는 트랜잭션 객체를 반환한다.
IDBDatabase.transaction(store_names, mode, options);
// 예시
IDBDatabase.transaction(['store1', 'store2']); // 객체저장소가 여러개에 접근할 경우
IDBDatabase.transaction('store1');             // 객체저장소가 하나에 접근할 경우

(4) 예시 코드

function writeIdxedDB(names) {
  const request = window.indexedDB.open('SampleDB');
  request.onerror =(e)=> {
    alert('DataBase error', e.target.errorCode);
  }
  request.onsuccess =(e)=> {
    const db = request.result;
    const transaction = db.transaction(['name'], 'readwrite');  
    //person 객체 저장소에 읽기&쓰기 권한으로 transaction 생성

    // 완료, 실패 이벤트 처리
    transaction.oncomplete =(e)=> {
      console.log('success');
    }
    transaction.onerror =(e)=> {
      console.log('fail');
    }

    // transaction으로 
    const objStore = transaction.objectStore('name');
    for (const name of names) {
      const request = objStore.add(name);   // 저장
      request.onsuccess =(e)=> console.log(e.target.result);
    }  
  }
} 

const names = [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}];

writeIdxedDB(names);



4) 본격적으로 CRUD 해보기

(1) db에서 데이터 조회하기

  • 특정 데이터값을 조회하려면 해당 데이터의 key값을 사용하면 된다.
function getIdxedDBValue(key) {
  const request = window.indexedDB.open('SampleDB');  // 1. DB 열기
  request.onerror =(e)=> console.log(e.target.errorCode);

  request.onsuccess =(e)=> {
    const db = request.result;
    const transaction = db.transaction('name');      
    transaction.onerror =(e)=> console.log('fail');
    transaction.oncomplete =(e)=> console.log('success');

    const objStore = transaction.objectStore('name');
    const objStoreRequest = objStore.get(key);        // 2. get으로 데이터 접근
    objStoreRequest.onsuccess =(e)=> {
      console.log(objStoreRequest.result)
    }
  }
}
getIdxedDBValue(1);  // { id:1, name:"a" }

  • 전체 데이터를 조회하려면, cursor를 사용해 데이터를 조회할 수 있다.
function getIdxedDBValues() {
  const request = window.indexedDB.open('SampleDB');      // 1. DB 열기
  request.onerror =(e)=> console.log(e.target.errorCode);

  request.onsuccess =(e)=> {
    const db = request.result;
    const transaction = db.transaction('name');
    transaction.onerror =(e)=> console.log('fail');
    transaction.oncomplete =(e)=> console.log('success');

    const objStore = transaction.objectStore('name');    // 2. name 저장소 접근
    const cursorRequest = objStore.openCursor();
    cursorRequest.onsuccess =(e)=> {
      let cursor = e.target.result;
      if (cursor) {
        const value = objStore.get(cursor.key);         // 3. 커서를 사용해 데이터 접근
        value.onsuccess =(e)=> {
          console.log(e.target.result);
        }
        cursor.continue();                              // 4. cursor로 순회
      }
    }
  }
}
getIdxedDBValues();  // { id:1, name:"a" }, {id: 2, name: 'b'}, {id: 3, name: 'c'}

(2) db에서 데이터 수정하기

  • put()을 사용해 db의 데이터 값을 수정할 수 있다.
function updateIdxedDBValue(key, value) {
  const request = window.indexedDB.open('SampleDB');  // 1. db 열기
  request.onerror =(e)=> console.log(e.target.errorCode);

  request.onsuccess =(e)=> {
    const db = request.result;
    const transaction = db.transaction('name', 'readwrite');
    transaction.onerror =(e)=> console.log('fail');
    transaction.oncomplete =(e)=> console.log('success');

    const objStore = transaction.objectStore('name');// 2. name 저장소 접근
    const objStoreRequest = objStore.get(key);       // 3. key값으로 데이터 접근
    objStoreRequest.onsuccess =(e)=> {
      const updateRequest = objStore.put(value);     // 4. 수정
      updateRequest.onerror =(e)=> console.log('udpate error');
            updateRequest.onsuccess =(e)=> console.log('success');
    }
  }
}
updateIdxedDBValue(1, {id: 1, name: 'vvvv'}); 
updateIdxedDBValue(2, {id: 2, name: 'bbbbb'});

  • 단, put을 할 때, 키(key)를 포함한 객체를 넘겨야 한다.
  • 만약 key값을 넘기지 않고 값만 바꾸고 싶다면 아래 방법을 사용하자
...
objStoreRequest.onsuccess =(e)=> {
  const data = objStoreRequest.result;      // 해당 키의 데이터 다 가져오기
  data.name = '어쩌구저쩌구로 변경';             // 데이터에서 일부 속성값 변경
  const updateRequest = objStore.put(data); // 변경된 data를 넘김  
}

(3) db에서 데이터 삭제하기

  • delete()를 사용해 특정 키의 값 삭제할 수 있다.
function deleteIdxedDBValue(key) {
  const request = window.indexedDB.open('SampleDB');     // 1. db 열기
  request.onerror =(e)=> console.log(e.target.errorCode);

  request.onsuccess =(e)=> {
    const db = request.result;
    const transaction = db.transaction('name', 'readwrite');
    transaction.onerror =(e)=> console.log('fail');
    transaction.oncomplete =(e)=> console.log('success');

    const objStore = transaction.objectStore('name');   // 2. name 저장소 접근
    const objStoreRequest = objStore.delete(key);       // 3. 삭제하기 
    objStoreRequest.onsuccess =(e)=> {
      console.log('deleted');
    }
  }
}
deleteIdxedDBValue(1);

  • clear()를 사용해 특정 저장소의 데이터 값을 모두 삭제할 수 있다.
function clearIdxedDBValue() {
  const request = window.indexedDB.open('SampleDB');     // 1. db 열기
  request.onerror =(e)=> console.log(e.target.errorCode);

  request.onsuccess =(e)=> {
    const db = request.result;
    const transaction = db.transaction('name', 'readwrite');
    transaction.onerror =(e)=> console.log('fail');
    transaction.oncomplete =(e)=> console.log('success');

    const objStore = transaction.objectStore('name');   // 2. name 저장소 접근
    const objStoreRequest = objStore.clear();           // 3. 전체 삭제
    objStoreRequest.onsuccess =(e)=> {
      console.log('cleared');
    }
  }
}
clearIdxedDBValue();

반응형

댓글