IndexedDB는 파일이나 블롭 등 많은 양의 구조화된 데이터를 클라이언트에 저장하기 위한 로우 레벨 API이다. IndexedDB API는 인덱스를 사용해 데이터를 고성능으로 탐색할 수 있다. Web Storage는 적은 양의 데이터를 저장하는데 유용하지만 많은 양의 구조화된 데이터에는 적합하지 않은데, 이런 상황에서 IndexedDB를 사용할 수 있다.
ndexedDB는 SQL을 사용하는 관계형 데이터베이스(RDBMS)와 같이 트랜잭션을 사용하는 데이터베이스 시스템이다. 그러나 IndexedDB는 RDBMS의 고정컬럼 테이블 대신 JavaScript 기반의 객체지향 데이터베이스이다. IndexedDB의 데이터는 인덱스 키를 사용해 저장하고 검색할 수 있으며, 구조화된 복사 알고리즘을 지원하는 객체라면 모두 저장할 수 있다. 사용하려면 데이터베이스 스키마를 지정하고, 데이터베이스와 통신을 연 후에, 일련의 트랜잭션을 통해 데이터를 가져오거나 업데이트해야 한다.
[장점]
[단점]
웹 저장소 API보다 사용법이 더 복잡하다
IndexedDB가 권장하는 기본패턴은 다음과 같다.
Database
- 브라우저는 여러개의 Database를 가질 수 있다.
- Database에는 Version 정보가 있고, 여러개의 ObjectStore를 가질 수 있다.
- Database 수정시에는 Version을 수정해야 한다.- indexedDB.open(db_name, version) 함수로 Database를 열도록 요청한다.
ObjectStore
- 데이터를 담는 공간으로, 여러개의 레코드(Key-Value)를 가진다.
- ObjectStore의 이름은 고유해야 한다.
- createObjectStore() 함수로 만든다.
IndexedDB를 사용하는 것은 다른 브라우저 저장 방식을 사용하는 것보다 더 복잡하다. 어떤 데이터를 생성/읽기변경/삭제하기 전에, 먼저 데이터베이스를 열고, 필요한 스토어(데이터베이스의 테이블과 유사한)를 생성해야 한다.
var request = window.indexedDB.open("MyDatabase");
open 요청은 데이터베이스를 즉시 열거나 즉시 트랜잭션을 시작하지 않는다.
open() 함수를 호출하면 이벤트로 처리한 결과(성공 상태)나 오류 값이 있는 IDBOpenDBRequest 객체를 반환한다. open() 함수의 결과는 IDBDatabase 의 인스턴스다.
var DATABASE = 'MyDatabase';
var DB_VERSION = 1;
var request = indexedDB.open(DATABASE, DB_VERSION);
open 메소드의 두번째 매개 변수는 데이터베이스의 버전이다. 데이터베이스의 버전은 데이터베이스 스키마를 결정한다. 데이터베이스 스키마는 데이터베이스 안의 객체 저장소와 그것들의 구조를 결정한다.
모든 요청에 대해 성공했을 때, 그리고 에러가 발생했을 때 제어를 할 객체를 요청해야 된다.
request.onerror = function(event) {
// request.errorCode 에 대해 무언가를 한다!
};
request.onsuccess = function(event) {
// request.result 에 대해 무언가를 한다!
};
성공하면, request를 target으로 갖는 success 이벤트 (즉, type 속성이"success" 로 설정된 DOM 이벤트)가 발생한다. 실행되면, request 의 onsuccess() 함수는 success 이벤트를 인수로 트리거된다.
반면, 문제가 있는 경우, 오류 이벤트 (즉 type 속성이"error" 로 설정된 DOM 이벤트)가 발생하고, 이 오류 이벤트를 인수로 onerror() 함수가 트리거된다.
위에서, 데이터베이스 생성 요청을 허용하여 success 콜백을 트리거하는 success 이벤트를 받았다고 가정한다면, 이후에, IDBDatabase 의 인스턴스인 request.result 를 사용하기 위해 저장하는 코드를 작성하면 아래와 같다.
var db;
var request = indexedDB.open("MyTestDatabase");
request.onerror = function(event) {
// request.errorCode 에 대한 무언가..
};
request.onsuccess = function(event) {
db = request.result;
};
새로운 데이터베이스를 만들거나 기존 데이터베이스의 버전 번호를 높일 때(데이터베이스 열기시, 이전 버전보다 높은 버전 번호를 지정하면), onupgradeneeded 이벤트가 트리거된다.
# request.result에 설정된 onversionchange 이벤트 핸들러에 IDBVersionChangeEvent 객체가 전달된다.
이 버전의 데이터베이스에 필요한 객체 저장소(ObjectStore)를 만들거나 삭제 할 때, upgradeneeded 이벤트 처리기에서 진행해야 한다.
onsuccess 는 이 이벤트가 끝나면 발생된다.
// This event is only implemented in recent browsers
request.onupgradeneeded = function(event) {
// Save the IDBDatabase interface
// event.target.result === request.result
var db = event.target.result;
// Create an objectStore for this database
var objectStore = db.createObjectStore("test", { keyPath: "myKey" });
};
- 기존 객체 저장소를 변경(예, keyPath를 변경) 해아 하는 경우, 이전 객체 저장소를 삭제하고 새 옵션으로 다시 만들어야한다.
- 이미 존재하는 이름으로 객체 저장소를 만들려고 하면 (또는 존재하지 않는 객체 저장소를 삭제하려고 하면) 오류가 발생한다.
데이터베이스 열었다면, 이제 데이터베이스를 구축한다.
IndexedDB는 테이블이 아닌 객체 저장소를 사용하며 하나의 데이터베이스는 여러 개의 객체 저장소를 포함할 수 있다. 값을 객체 저장소에 저장할 때마다 값은 키와 연관된다. 객체 저장소가 키 경로(keyPath)
또는 키 생성기(autoIncrement)
옵션의 사용 여부에 따라 키를 제공할 수 있는 여러 가지 방법이 있다.
이 때, 객체 저장소가 객체를 보유하고 있으면 객체 저장소에서 인덱스를 만들 수 있다. 인덱스를 사용하면 객체의 키가 아닌 저장된 객체의 속성 값을 사용하여 객체 저장소에 저장된 값을 검색할 수 있다.
또한, 인덱스에는 저장된 데이터에 대한 간단한 제약 조건을 적용 할 수 있는 기능이 있다. 인덱스를 작성할 때 고유(unique) 플래그를 설정하면, 인덱스는 인덱스의 키 경로에 대해 동일한 값을 갖는 두 개의 객체가 저장되지 않도록 보장한다.
예를 들자면, 사람 집단을 보유하고 있는 객체 저장소에서 동일한 email 주소를 갖지 못 한다는 것을 보장하려는 경우, 이를 강제하기 위해 고유(unique) 플래그 설정한 인덱스를 사용하면 된다.
var request = indexedDB.open("MyDatabase", 1);
request.onerror = function(event) {
// Handle errors.
};
request.onupgradeneeded = function(event) {
var db = event.target.result;
// 1.
var objectStore = db.createObjectStore("customers", { keyPath: "id" });
// 2.
objectStore.createIndex("name", "name", { unique: false });
objectStore.createIndex("email", "email", { unique: true });
};
객체 저장소는 createObjectStore()를 한번 호출함으로써 생성된다.
이 메소드는 저장소의 이름과 파라미터 객체를 인자로 받는다. 파라미터 객체는 선택적으로 사용할 수 있지만, 이는 중요한 설정들을 정의하고 만들고자하는 객체 저장소의 타입을 정의하기 때문에 매우 중요하다.
위의 예시를 보면, 객체 저장소의 이름을 "customers"로 짓고 개별 객체들이 유일하게 저장되도록 만들어주는 특성인 keyPath를 정의했다. 그리고, keyPath로 지정된 ssn 프로퍼티는 고유함이 보장되어야 하고, objectStore 에 저장되는 모든 객체에 반드시 포함되어야 한다.
저장된 객체의 name 프로퍼티를 찾기 위한 인덱스 "name"을 요청한다. createIndex() 도 생성하려는 인덱스의 종류를 결정하는 선택적인 객체인 options 을 인자로 받는다. name 프로퍼티가 없는 객체를 추가할 수는 있지만, 이 경우 그 객체는 "name" 인덱스에 나타나지 않는다.
새 데이터베이스에서 작업을 하기전에, 트랜잭션을 시작할 필요가 있다.
트랜잭션은 데이터베이스 객체 단위로 작동하므로 트랜잭션을 사용할 객체 저장소를 지정해줘야한다. 트랜잭션에 들어오고 나면, 자료가 있는 객체 저장소에 접근할 수 있고 요청을 만들 수 있다. 다음으로, 데이터베이스에 변경점을 만들지, 혹은 읽기만 할지 결정해야한다.
트랜잭션의 3가지 모드 : readonly, readwrite, versionchange.
4.1. 키 생성기 사용하기
객체 저장소를 생성할 때 autoIncrement 플래그를 설정함으로써 키 생성기를 활성화할 수 있다. 기본값으로 이 플래그는 설정되지 않는다.
키 생성기가 활성화되면, 객체 저장소에 값을 추가할 때 키가 자동으로 추가된다. 처음 생성되면 키 생성기의 값은 항상 1로 설정되고, 새로 생성되는 키는 기본적으로 이전 키에서 1을 더한 값이 된다. 키 생성기의 값은 트랜잭션이 취소되는 등 데이터베이스 작업이 복구되는게 아닌 한 절대 작아지지 않는다. 그래서 레코드를 지우거나 객체 저장소의 모든 레코드를 지우더라도 해당 객체 저장소의 키 생성기에는 영향을 끼치지 않는다.
var objStore = db.createObjectStore("names", { autoIncrement : true });
// The added records would be like:
// key : 1 => value : "Bill"
// key : 2 => value : "Donna"
customerData.forEach(function(customer) {
objStore.add(customer.name);
});
// 완료 및 실패 이벤트 제어
transaction.oncomplete = function(event) {
console.log("done");
};
transaction.onerror = function(event) {
console.log("fail");
};
var objectStore = transaction.objectStore("customers");
for (var i in customerData) {
var request = objectStore.add(customerData[i]);
request.onsuccess = function(event) {
console.log(event.target.result);
};
}
var key = "444-44-4444";
var transaction = db.transaction(["customers"]);
...
// 완료 및 실패 이벤트 제어
...
var objectStore = transaction.objectStore("customers");
var request = objectStore.get(key);
request.onerror = function(event) {
// Handle errors!
};
request.onsuccess = function(event) {
alert("Name for SSN 444-44-4444 is " + request.result.name);
};
var key = "444-44-4444";
var value = 42;
var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
var request = objectStore.get(key);
request.onsuccess = function(event) {
// update 하길 바라는 기존의 value 가져오기
var data = event.target.result;
// update
data.age = value;
// update 한 것 다시 database 에 넣기
var requestUpdate = objectStore.put(data);
requestUpdate.onerror = function(event) {
// Do something with the error
};
requestUpdate.onsuccess = function(event) {
// Success - the data is updated!
};
};
var key = "444-44-4444";
var request = db.transaction(["customers"], "readwrite")
.objectStore("customers")
.delete(key);
request.onsuccess = function(event) {
// It's gone!
};
https://developer.mozilla.org/ko/docs/Web/API/IndexedDB_API/Using_IndexedDB
https://lcs1245.tistory.com/entry/IndexedDB-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%A0%95%EB%A6%AC