IndexedDB에 대해 알아보자

브라우저 내부에 데이터베이스가 있다고?

2024-10-28 · 8분 · 조회 1

사내 Tech 스터디에서 발표된 내용을 짧게 정리하며 공부한 내용입니다.

IndexedDB란?

IndexedDB는 브라우저 내에서 대용량 데이터를 저장하고 관리할 수 있는 비관계형(NoSQL) 데이터베이스이다.

왜 사용할까?

localStorage, sessionStorage

  • 동기적으로 작동하며
  • 문자열로만 저장할 수 있어
  • JSON.stringify, JSON.parse 를 사용해야 하며
  • 저장용량이 적다

는 단점이 있는 반면

IndexedDB

  • 비동기적으로 작동하며
  • 트랜잭션을 사용하며
  • 키, 값 형태로 js가 인식할 수 있는 모든 데이터 타입을 저장할 수 있으며
  • 대용량 데이터를 저장할 수 있다

는 장점이 있다.

IndexedDB 구조

Database

  • Version과 N개의 Object Store 를 가진다.
  • 브라우저는 여러 Database 를 가질 수 있다.
  • indexedDB.open(db_name, version)으로 열수 있다.

Object Store

  • 데이터를 담는 공간이다.
  • N개의 레코드(Key-Value) 를 가질 수 있다.
  • Value의 형태는 다른 Value들과 일치하지 않아도 된다.
  • 레코드는 Key에 따라 오름차순으로 정렬된다.
  • Object Store 이름은 고유 해야한다.
  • IDBRequest.createObjectStore(store_name, \{keyPath: 'id'\}) 로 만들 수 있다.
  • Object store 에 key path 를 설정하면, in-line keys (내부 key) 를 사용하며, 그 외에는 out-of-line keys (외부 key) 를 사용한다.

IndexedDB API

Transaction

  • IndexedDB API 작업은 transaction contect 내에서 발생한다.
  • transaction contect 내에서 작업이 실패하면, 해당 작업 상태는 적용되지 않고, 이전 상태로 돌아간다.
  • 만약 transaction 외부에서 IndexedDB API 를 호출하면, 에러가 발행한다.
  • IDBRequest.transaction 로 만들수 있다.
  • transaction 은 readwrite, readonly, versionchange 상태를 가질 수 있다.

Cursor

  • Cursor 는 Object Store 내의 레코드를 순회할 수 있는 방법이다.
  • IDBObjectStore.openCursor()에 key, keyRange를 넣어 호출하고, IDBCursorWithValue.continue()로 다음 레코드로 이동할 수 있다.

Index

  • Object Store 에서 특정 필드를 인덱싱하여 검색을 빠르게 할 수 있다.
  • IDBObjectStore.createIndex(index_name, keyPath) 로 만들 수 있다.
  • Object Store에 Index와 관련된 레코드가 업데이트 되면, Index도 업데이트 된다.

예제

DB 생성

indexedDB.open으로 DB를 생성할 수 있으며, 비동기로 처리되어 success 시의 이벤트를 처리할 수 있다.\

const request = window.indexedDB.open('myDB', 1);
 
request.onerror = (event) => {
  console.log('DB Error', event);
};
 
request.onupgradeneeded = (event) => {
  const db = event.target.result;
  const objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id' });
  objectStore.createIndex('name', 'name', { unique: false });
};
 
request.onsuccess = (event) => {
  const db = event.target.result;
  console.log('DB Opened', db);
};
  • onupgradeneeded
    • Name 또는 Version과 일치하는 데이터베이스가 없는 경우 호출되며 데이터베이스를 생성한다.
  • onsuccess
    • Name과 Version 모두 일치하는 데이터베이스가 있는 경우 호출된다.
  • error
    • Name이 일치하지만 존재하는 DB의 Version 보다 낮은 Version을 호출하면 error가 발생한다.

Object Store 생성

버전이 변경되었을 때 실행되는 upgradeneeded 이벤트와 함께 스토어를 생성하며, 식별자(key-path)를 지정한다.

이 때 버전을 기준으로 스토어를 추가해주거나 삭제하는 것도 가능하다.

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  const objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id' });
 
  if (event.oldVersion < 2) {
    db.createObjectStore('myObjectStore2', { keyPath: 'id' });
  }
};

데이터 추가하기

IDBObjectStore.add 메서드를 사용하여 데이터를 추가할 수 있다.

<button
  onClick={() => {
    const request = db
      .transaction(['myObjectStore'], 'readwrite')
      .objectStore('myObjectStore')
      .add({ id: 1, name: 'John', age: 30 });
 
    request.onsuccess = (event) => {
      console.log('Data added', event);
    };
  }}
/>

데이터 조회하기

IDBObjectStore.get 메서드를 사용하여 데이터를 조회할 수 있다.

<button
  onClick={() => {
    const request = db.transaction(['myObjectStore'], 'readonly').objectStore('myObjectStore').get(1);
 
    request.onsuccess = (event) => {
      console.log('Data fetched', event.target.result);
    };
  }}
/>

결과값

#키(키 경로: id)
11{ id: 1, name: 'John', age: 30 }

데이터 수정하기

IDBObjectStore.put 메서드를 사용하여 데이터를 수정할 수 있다.

<button
  onClick={() => {
    const request = db
      .transaction(['myObjectStore'], 'readwrite')
      .objectStore('myObjectStore')
      .put({ id: 1, name: 'John', age: 31 });
 
    request.onsuccess = (event) => {
      console.log('Data updated', event);
    };
  }}
/>

결과값

#키(키 경로: id)
11{ id: 1, name: 'John', age: 31 }

데이터 삭제하기

IDBObjectStore.delete 메서드를 사용하여 데이터를 삭제할 수 있다.

<button
  onClick={() => {
    const request = db.transaction(['myObjectStore'], 'readwrite').objectStore('myObjectStore').delete(1);
 
    request.onsuccess = (event) => {
      console.log('Data deleted', event);
    };
  }}
/>

결과값

#키(키 경로: id)

데이터 조회하기 (Cursor)

IDBObjectStore.openCursor 메서드를 사용하여 데이터를 조회할 수 있다.

<button
  onClick={() => {
    const request = db.transaction(['myObjectStore'], 'readonly').objectStore('myObjectStore').openCursor();
 
    request.onsuccess = (event) => {
      const cursor = event.target.result;
      if (cursor) {
        console.log('Data fetched', cursor.value);
        cursor.continue();
      }
    };
  }}
/>

실제 사용 예시

  • 직접 indexedDB를 다루기보다는 라이브러리를 통해 컨트롤 한다.
    • Dexie.js 라이브러리를 사용하면 indexedDB를 쉽고 간편하게 다룰 수 있다.
  • Slack (Web)
    • redux persist를 위한 공간으로 사용한다.
    • 채널 리스트, 읽음 여부 등의 정보를 저장하는 것 같다.
  • Youtube
    • permission이나 idtoken, guide section에 rendering할 데이터들 등을 저장하는 것 같다.

결론

  • indexedDB는 대용량 데이터를 저장하고 관리할 수 있는 브라우저 내부 데이터베이스이다.
  • localStorage, sessionStorage와 달리 비동기적으로 작동하며, 트랜잭션을 사용하여 데이터를 저장하고 관리할 수 있다.
  • indexedDB를 직접 다루기보다는 라이브러리를 사용하여 컨트롤하는 것이 좋다.
  • 적재적소에 사용하면 좋을 것 같다.

Ref.

Home
Posts
Notes