토이 프로젝트를 진행하기 위해 필요한 Web Database에 대해서 알아보던 중,
Local Storage와 IndexedDB에 대해서 알게 되었다.
진행하려는 토이 프로젝트에는 IndexedDB가 적합할 것 같아서, 조금 더 공부해보고 정리해보자!
📌 이 글의 모든 내용은 MDN Web Docs의 내용을 공부하며 정리한 글입니다.
IndexedDB는 파일이나 블롭 등 많은 양의 구조화된 데이터를 클라이언트에 저장하기 위한 로우 레벨 API입니다. (MDN Web Docs, IndexedDB)
Web Database의 종류 중에 많은 양의 데이터를 저장하기에 적합하다는 것이 내게 큰 특징으로 다가왔다. 이 외에 다른 특징들을 살펴보자면,
먼저 IndexedDB를 사용하는 기본패턴은 아래와 같다.
1. 데이터베이스를 열고
2. 객체 저장소(Object store)를 생성한 뒤
3. 트랜잭션(Transaction)을 시작하고 데이터베이스 작업(데이터 읽기/추가 등)을 요청한다.
4. DOM EventListner
를 사용하여 요청이 완료될때가지 기다리고 결과를 확인한다.
그럼 이제 코드를 통해서 알아보자.
const request = indexedDB.open("notes", 2);
// IndexedDB.open(Name, Version)
request.onupgradeneeded = e => {
alert("upgraed is called");}
request.onsuccess = e => {
alert("success is called");}
request.error = e => {
alert("error is called");}
IndexedDB.open(Name, Version)
함수를 통해서 DB open이 가능하다.
위의 세가지 케이스를 좀 더 자세히 살펴보자면,
request.onupgradeneeded = e => {
db = e.target.result;
var objectStore = db.createObjectStore("memo", { keyPath: "id" });
}
createObjectStore(tableName)
함수를 이용하면 우리가 알고 있는 테이블을 만들 수 있다.
위의 코드처럼 onupgraedneeded 부분을 수정한 뒤 실행하면 notes 저장소에 personal_notes, todo_notes라는 테이블이 생성된다.
request.onupgradeneeded = e => {
// 중간 생략
objectStore.createIndex("name", "name", { unique: false });
}
createIndex()
의 unique 속성을 이용하면 해당 객체를 unique key로 만들 수 있다.
transaction()
함수로 transaction을 시작하고,objectStore()
함수로 테이블 선택 및 add()
함수로 원하는 객체를 추가한다.const memos = [
{ id: 1, name: "Lee", age: 12, text:"I don't want to go to school."},
{ id: 2, name: "Kim", age: 25, text:"I don't want to go to work." }
];
var memoObjectStore = db.transaction("memo", "readwrite").objectStore("memo");
memos.forEach(function(memo) {
memoObjectStore.add(memo);
});
❗️ key 값이 같은 경우, ConstraintError
를 발생시키며 해당 트랜잭션을 Abort 시킨다. 위의 예제에서는 key=id 이므로 id값을 동일한 값으로 설정시켜 실행하면 ConstraintError
를 확인할 수 있다.
transaction()
의 기본값은 readonly이기 때문에 생략했다.get()
을 이용해 데이터를 읽는다.var memoObjectStore = db.transaction("memo").objectStore("memo");
var request = memoObjectStore.get("1");
request.onerror = e => {
alert("Error is called");
};
request.onsuccess = e => {
alert(`Name: ${request.result.name}, Text: ${request.result.text}`);
}
transaction()
은 readwrite 모드로 설정해주고put()
을 이용해 데이터를 업데이트 할 수 있다.let memoObjectStore = db.transaction("memo", "readwrite").objectStore("memo");
let request = memoObjectStore.get("1");
request.onerror = e => {
alert("Error is called");
};
request.onsuccess = e => {
let data = request.result;
data.text = "I don't want to travel";
let requestUpdate = memoObjectStore.put(data);
requestUpdate.onerror = e => {
alert("Error is called");
}
requestUpdate.onsuccess = e => {
alert("Success Updating");
}
}
get()
을 이용해 데이터를 조회하려면 key를 알고 있어야한다.
그렇다면 key를 모르는 상태에서 전체 데이터를 조회하려면? cursor를 이용하면 된다.
let objectStore = db.transaction("memo").objectStore("memo");
let request = objectStore.openCursor();
request.onerror = e => {
alert("Error with opening cursor! " + e.target.error)
}
request.onsuccess = e => {
let cursor = e.target.result;
if(cursor){
alert(`Key: ${cursor.key}, Name: ${cursor.value.name}, Text: ${cursor.value.text}`);
cursor.continue();
}
else{
alert("No more entries!");
}
}
cursor를 이용해 다음 값을 조회하고 싶다면 cursor.continue()
를 사용한다.
Mozila에서 비공식적으로
getAll()
을 이용해 전체 값을 조회할 수 있음을 말하고 있는데, 이는 IndexedDB의 표준이 아니고 추후 없어질 가능성이 있기 때문에 권장하지 않고 있다.
getAll()
은 전체 객체를 한번에 생성하기 때문에 전부 조회하는 용도로 사용해도 무방하나, 각 객체 또는 일부를 살펴보기에는cursor
를 사용하는 것이 더 효율적이다.
좋은글에서 잘 배우고갑니다!