1. localStorage
브라우저에 따라서 10~20MB까지의 데이터를 저장할 수 있다.
localStorage는 key-value형식으로 key와 value모두 UTF-16 문자열인 (탭을 닫아도 유지되는)영구적인 저장장치이다.
localStorage는 유저가 브라우저에서 영구적인 데이터 저장을 막는 등 보안 설정에 위배되면 에러를 반환한다.
domain이 다르면 데이터에 접근할 수 없다.
메소드 | 설명 |
localStorage.setItem("myCat", "Tom"); | 현재 도메인의 지역 저장 장치에 데이터 추가 |
const cat = localStorage.getItem("myCat"); | 해당 데이터 얻기 |
localStorage.removeItem("myCat"); | 해당 데이터 삭제 |
localStorage.clear(); | 모든 데이터 삭제 |
localStorage.length; | 데이터의 수 |
2. sessionStorage
브라우저에 따라서 10~20MB까지의 데이터를 저장할 수 있다.
sessionStorage는 key-value형식으로 key와 value모두 UTF-16 문자열인 휘발성 저장장치이다.
세션 쿠키와 다르게 sessionStorage는 새로운 탭을 열때마다 새로운 session을 만든다.
메소드 | 설명 |
sessionStorage.setItem("key", "value"); | 현재 도메인의 지역 저장 장치에 데이터 추가 |
let data = sessionStorage.getItem("key"); | 해당 데이터 얻기 |
sessionStorage.removeItem("key"); | 해당 데이터 삭제 |
sessionStorage.clear(); | 모든 데이터 삭제 |
sessionStorage.length; | 데이터의 수 |
3. indexedDB
storage는 적은 양의 데이터를 저장하는데 유용하다. 대부분 10~20메가를 넘지 못한다. 하지만 indexedDB는 대용량의 데이터를 저장하는데 유용하다.
indexedDBsms sql기반의 RDBMS처럼 transactional 데이터베이스 시스템이다. 그러나 RDBMS와 다르게 자바스크립트 기반 객체지향 데이터베이스이다.
indexedDB를 사용하는 명령은 비동기적으로 작동한다.
domain이 다르면 데이터에 접근할 수 없다.
다른 데이터베이스와 비슷하게 사용할 생각이라면, indexedDB를 사용하는게 힘들 수 있다. 다음의 indexedDB의 특징을 잘 알아 두어야 한다.
- indexedDB 데이터베이스는 key-value쌍으로 저장한다. value들은 복잡한 objects가 가능하다.
- indexedDB는 transactional 데이터베이스 모델에 의거한다.
- indexedDB API는 대부분 비동기적이다.
- indexedDB는 많은 요청을 사용한다.
- indexedDB는 결과가 가능할 때 DOM이벤트로 알려준다.
- indexedDB는 객체지향이다.
- indexedDB는 SQL을 사용하지 않는다.
- indexedDB는 same-origin 정책에 부착된다.
용어
Database 용어 | 설명 |
database | 정보의 저장소. 통상적으로 하나 이상의 object stores를 포함한다. Name과 Current version을 반드시 가지고 있어야 함. |
database connection | db를 열어서 생성된 작업. db마다 여러 connection을 동시에 만들 수 있다. |
durable | durable은 firefox에서 읽고쓰기 트랙젝션 complete이벤트가 디스크에 적용된것을 보장한 후에 발생한다는 것이다. |
index | 다른 object store에 있는 레코드를 조회하기 위한 전문 개체 저장소이다. 또는, object store에 있는 레코드를 조회하기 위해 key또한 사용할 수 있다. |
object store | 데이터베이스에 데이터가 저장되는 메커니즘이다. |
request | 데이터베이스에서 읽기와 쓰기가 끝난 작업이다. |
transaction | 특정한 데이터베이스에서의 데이터 접근과 데이터 변경 작업의 원자적 집합이다. |
version | 데이터베이스가 처음 만들어졌을 때, 버전은 정수 1이다. |
key and value 용어 | 설명 |
in-line key | 저장된 값의 일부로서 저장된 key. |
key | 저장된 값을 정리하여 object store에 검색하는 데이터 값. 또는, object store에 있는 레코드를 조회하기 위해 index를 사용할 수 있다. |
key generator | 순서대로 새 키를 생성하는 매커니즘이다. |
key path | 브라우저가 object store 또는 index에서 key를 추출할 위치를 정의한다. |
out-of-line key | 저장중인 값과 별도로 저장되는 키. |
value | 각 레코드는 js에서 표현할 수 있는 어떠한 것이든 value를 가진다. |
Range and scope 용어 | 설명 |
cursor | key range를 가진 여러 레코드에서 반복하는 메커니즘이다. |
key range | 키에 사용되는 일부 데이터 유형에 대한 연속적인 간격이다. |
scope | 트랜잭션이 적용되는 object stores와 indexes의 집합이다. |
indexedDB를 사용하는 기본적인 순서:
1. 데이터베이스를 연다.
2. 데이터베이스에 object store를 생성한다.
3. 트랜잭션을 시작하고 데이터베이스에 작업을 요청한다.
4. 작업 완료를 기다린다.
5. 결과로 무언가를 한다.
- 데이터베이스를 연다.
const request = window.indexedDB.open("MyTestDB", 3);
open요청은 데이터베이스를 열거나 트랜잭션을 시작하지 않는다. open함수는 IDBOpenDBRequest obejct를 반환한다.
- 핸들러 생성 & 에러 처리
let db;
const request = indexedDB.open("MyTestDB");
request.onerror = (event) => {
console.error(`Database error: ${event.target.errorCode}`);
}
request.onsuccess = (event) => {
db = event.target.result;
};
request.result는 IDBDatabase의 인스턴스가 된다.
- 데이터베이스에 object store를 생성한다.
request.onupgradeneeded = (event) => {
// Save the IDBDatabase interface
const db = event.target.result;
// Create an objectStore for this database
const objectStore = db.createObjectStore("name", { keyPath: "myKey" });
};
onupgradeneeded는 새로운 데이터베이스를 만들거나 기존 데이터베이스의 버전을 높일 때 트리거된다.
onupgradeneeded가 성공하면 onsuccess핸들러가 실행된다.
- 데이터베이스 구조화하기
indededDB는 table 대신 object store를 사용한다. object store에 값을 저장할 때 마다 값은 key에 연관된다.
object store가 key path와 key generator를 사용하는지에 따라 key를 제공하는 여러 방법이 있다.
Key Path (keyPath) |
Key Generator (autoIncrement) |
설명 |
x | x | 숫자와 문자열같은 원시적인 것들을 포함한 어떠한 값들도 object store에 저장할 수 있다. 새로운 값을 추가할 때마다 key를 제공해야 한다. |
o | x | js object만 값으로 object store에 저장할 수 있다. object들은 key path와 같은 이름을 가진 속성을 가져야 한다. |
x | o | 어떠한 값도 object store에 저장할 수 있다. key는 자동으로 생성된다. 또는 key를 제공하면 해당 key가 된다. |
o | o | js object만 값으로 object store에 저장할 수 있다. 보통 key generator가 값을 생성하고 key path를 이름으로 가지는 속성을 object에 생성한다. |
또한 우리는 object store에 index들을 만들 수 있다. index를 사용해 key대신에 object store에 있는 값들을 볼 수 있다.
또한 unique 플래그를 사용해 간단한 제약을 줄 수 있다. unique 플래그를 true로 하면 값이 겹칠 수 없게 제약한다.
const customerData = [
{ ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
{ ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" },
];
const dbName = "the_name";
const request = indexedDB.open(dbName, 2);
request.onerror = (event) => {
// Handle errors.
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 고객 정보를 저장하기 위해 objectStore생성
// ssn을 keyPath로 사용해 유일한 값인것을 보장한다.
const objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
// 이름으로 고객 정보를 검색하기 위한 index생성. 값은 겹칠 수 있다.
objectStore.createIndex("name", "name", { unique: false });
// email로 고객을 검색하기 위한 index생성. 값은 겹칠 수 없게 한다.
objectStore.createIndex("email", "email", { unique: true });
// transaction oncomplete를 사용해 데이터를 넣는것이 objectStore 생성 후를 보장한다.
objectStore.transaction.oncomplete = (event) => {
// Store values in the newly created objectStore.
const customerObjectStore = db
.transaction("customers", "readwrite")
.objectStore("customers");
customerData.forEach((customer) => {
customerObjectStore.add(customer);
});
};
};
이제 우리는 고객 객체를 검색하기 위해 ssn을 사용하거나 index에서 그들의 이름을 사용할 수 있다.
object store를 만들 때 autoIncrement 플래그를 사용하면 그 object store에 key generator를 가능하게 한다. 기본적으로 이 값은 세팅되어있지 않다.
const request = indexedDB.open(dbName, 3);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// names라는 object store를 autoIncrement플래그를 true로 생성한다.
const objStore = db.createObjectStore("names", { autoIncrement: true });
// key generator가 있으므로 값을 넣을 때마다 key가 1부터 1씩 더해지며 자동으로 생성된다.
// key : 1 => value : "Bill"
// key : 2 => value : "Donna"
customerData.forEach((customer) => {
objStore.add(customer.name);
});
};
- 데이터 추가하기, 검색하기, 삭제하기
데이터베이스에서 무엇을 하기 전데, 트랜잭션을 시작해야 한다. 트랙잭션은 세가지 모드가 있다: readonly, readwrite, versionchange.
object store나 index를 만들거나 제거하는 것을 포함해 데이터베이스의 구조를 바꾸기 위해서는 트랜잭션을 versionchange모드로 해야한다.
object store에서 레코드를 읽으려면 트랜잭션을 readonly또는 readwrite로 하면 되고, 변경을 하려면 readwrite모드로 해야 한다.
아무 모드를 명시하지 않는다면, 기본적으로 readonly모드가 된다.
object store마다 readonly트랜잭션은 여러개가 있을 수 있지만 readwrite트랜잭션은 하나만 있을 수 있다. readwrite는 필요할 때만 사용하자.
const transaction = db.transaction(["customers"], "readwrite");
transaction함수는 두개의 인자를 받는다. 첫번째 인자는 object store들의 리스트이다. 두번째 인자는 트랜잭션 모드이다.
transaction은 error, abort, complete세가지 DOM 이벤트를 받을 수 있다.
// Do something when all the data is added to the database.
transaction.oncomplete = (event) => {
console.log("All done!");
};
transaction.onerror = (event) => {
// Don't forget to handle errors!
};
const objectStore = transaction.objectStore("customers");
customerData.forEach((customer) => {
const request = objectStore.add(customer);
request.onsuccess = (event) => {
// event.target.result === customer.ssn;
};
});
add함수로 데이터를 추가하는 예제이다.
const request = db
.transaction(["customers"], "readwrite")
.objectStore("customers")
.delete("444-44-4444");
request.onsuccess = (event) => {
// It's gone!
};
delete함수로 데이터를 삭제하는 예제이다.
const transaction = db.transaction(["customers"]);
const objectStore = transaction.objectStore("customers");
const request = objectStore.get("444-44-4444");
request.onerror = (event) => {
// Handle errors!
};
request.onsuccess = (event) => {
// Do something with the request.result!
console.log(`Name for SSN 444-44-4444 is ${request.result.name}`);
};
get함수로 데이터를 얻는 예제이다.
const objectStore = db
.transaction(["customers"], "readwrite")
.objectStore("customers");
const request = objectStore.get("444-44-4444");
request.onerror = (event) => {
// Handle errors!
};
request.onsuccess = (event) => {
// Get the old value that we want to update
const data = event.target.result;
// update the value(s) in the object that you want to change
data.age = 42;
// Put this updated object back into the database.
const requestUpdate = objectStore.put(data);
requestUpdate.onerror = (event) => {
// Do something with the error
};
requestUpdate.onsuccess = (event) => {
// Success - the data is updated!
};
};
put함수는 기존에 있는 데이터를 업데이트하거나, 데이터가 없으면 새로 입력한다. 위 예제는 기존 데이터를 바꾸는 것이다.
get함수는 key를 명시해야한다. 모든 값을 검색하고싶다면 cursor를 사용할 수 있다.
const objectStore = db.transaction("customers").objectStore("customers");
objectStore.openCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log(`Name for SSN ${cursor.key} is ${cursor.value.name}`);
cursor.continue();
} else {
console.log("No more entries!");
}
};
※getAll함수를 사용할 수 도 있다. key를 각각 보는것에 관심있다면 cursor가 좋다. 모든 데이터를 배열에 담을것이라면 getAll함수가 좋다.
index 사용하기.
key가 아닌 속성을 검색할 때, cursor를 사용해 모두 검사하기는 느리고 귀찮다. index를 대신 사용할 수 있다.
// 먼저, request.onupgradeneeeded로 index를 생성해주세요:
// objectStore.createIndex("name", "name");
// 그렇지 않을 경우 DOMException이 발생합니다.
const index = objectStore.index("name");
index.get("Donna").onsuccess = (event) => {
console.log(`Donna's SSN is ${event.target.result.ssn}`);
};
위 방법을 사용하면 같은 이름을 가진 여러 데이터들을 검색하지 못한다. 하나의 결과만 반환하기 때문이다.
여러 데이터들을 검색하고싶으면 cursor를 사용할 수 있다.
// 일반적인 커서를 사용해서 고객 레코드 전체를 가져오기
index.openCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
// cursor.key는 "Bill"과 같은 이름이며, cursor.value는 객체 전체를 의미합니다.
console.log(
`Name: ${cursor.key}, SSN: ${cursor.value.ssn}, email: ${cursor.value.email}`,
);
cursor.continue();
}
};
// 키 커서를 사용해서 고객 레코드 객체 키를 가져오기
index.openKeyCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
// cursor.key는 "Bill"과 같은 이름이며, cursor.value는 사회보장번호(SSN)입니다.
// 저장된 객체의 나머지 부분을 직접적으로 가져올 방법은 없습니다.
console.log(`Name: ${cursor.key}, SSN: ${cursor.primaryKey}`);
cursor.continue();
}
};
참고: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
https://developer.mozilla.org/en-US/docs/Web/API/Storage
https://developer.mozilla.org/en-US/docs/Web/API/Window/indexedDB
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
-
'web' 카테고리의 다른 글
Video.js (0) | 2024.09.01 |
---|---|
canvas 프레임(fps) 측정하기, 보장하기 (0) | 2024.06.04 |
브라우저 렌더링 (0) | 2024.05.13 |
Chrome DevTools (크롬 개발자도구) (0) | 2024.03.28 |
audio + input range type + 소리 조절 (0) | 2022.05.28 |