일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- open in browser
- jsp
- 실시간 상태값 저장
- spring boot
- 데이터 시각화
- 자바
- 여러 종류의 사용자 정의 함수
- 초보 개발자
- git
- MYSQL
- IndexedDB
- 자바빈
- chart.js
- github
- 자바스크립트
- vscode
- @requstbody
- tomcat
- resutful api
- java
- html
- 게시판
- chart.js 라이브러리
- 상태값 저장 유지
- ui인터페이스
- vsc
- Eclipse
- thymeleaf
- css
- JavaScript
- Today
- Total
수월한 IT
localStorage VS indexedDB를 사용한 현재 상태값 실시간 저장하기 본문
안녕하세요 IT여행자입니다.
이번 여행지는 제목과 같이 localStorage 또는 indexedDB를 사용하여 현재 상태값을 실시간으로 저장하고 필요할 때 재 사용할 수 있는 방법을 비교하면서 각각의 사용법을 알아보도록 하겠습니다.
localStorage와 indexedDB의 특징을 대강 정리하면 아래의 표와 같습니다.
[준비물]
개발툴 : VSC(Visual Studio Code)와 VSC의 extension 중 하나인 Live Server Extension만 있으면 될 것 같습니다.
적당한 폴더를 만들고 VSC를 구동시켜 놓습니다.
왼쪽 그림과 같이 폴더와 파일을 생성한 후 여행을 시작하겠습니다.
main.css는 공통으로 사용되는 스타일시트이고, localStorage.html과 indexedDB.html 파일 역시 동일한 html 코드이지만 사용할 자바스크립트 파일이 서로 달라 따로 작성하였습니다.
localStrorage.js와 indexedDB.js가 오늘 주로 살펴보아야 할 파일들입니다.
전체 코드 : https://github.com/hiparkwg/localStorageVSindexedDB.git
영상 : https://youtu.be/dBus3ak-agM
localStorage를 사용한 상태값 저장
1. 먼저 localStorage.html 코드와 main.css 코드를 작성하고 VSC에 설치된 Live Server Extension을 구동하면 아래와 같은 웹 페이지가 보입니다.
[html/localStorage.html]
<!-- file name : /html/localStorage.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>localStorage</title>
<link rel="stylesheet" href="/css/main.css">
<script defer src="/js/localStorage.js"></script>
</head>
<body>
<main id="main">
<header>
<h2>localStorage VS IndexedDB를 사용한 실시간 저장</h2>
</header>
<section>
<form name="frm" id="frm">
<span>1. 1+1의 정답은 ? </span><br/>
<label><input type="radio" name="test1" value="0">0</label>
<label><input type="radio" name="test1" value="1">1</label>
<label><input type="radio" name="test1" value="2">2</label>
<label><input type="radio" name="test1" value="3">3</label>
<br/><br/>
<span>2. 2+2의 정답은 ? </span><br/>
<label><input type="radio" name="test2" value="1">1</label>
<label><input type="radio" name="test2" value="3">3</label>
<label><input type="radio" name="test2" value="5">5</label>
<label><input type="radio" name="test2" value="없음">없음</label>
<br/><br/>
<span>3. 동물을 모두 고르시오 ? </span><br/>
<label><input type="checkbox" name="test3" value="강아지">강아지</label>
<label><input type="checkbox" name="test3" value="고양이">고양이</label>
<label><input type="checkbox" name="test3" value="장미">장미</label>
<label><input type="checkbox" name="test3" value="강아지풀">강아지풀</label>
<br/><br/>
<span>4. 사랑과 우정의 경계를 논리적으로 풀어 내시오.</span>
<br/>
<textarea name="test4"></textarea>
<br/>
<span>5. 컴퓨터 언어가 인간 언어에 미치는 영향에는 어떤것이 있을까 서술하시오.</span>
<br/>
<br/>
<textarea name="test5"></textarea>
<br/><br/>
<span>6. 광고 모델은 제품에 대해 어느정도까지 책임을 지어야 하는가.</span>
<br/>
<textarea name="test6"></textarea>
<br/><br/>
<div id="btnZone">
<button type="button" id="btnDelete">저장소 삭제</button>
</div>
</form>
</section>
</main>
</body>
</html>
[/css/main.css]
*{
box-sizing: border-box;
}
#main{
margin:auto;
width:600px;
min-height: 400px;
border:5px solid #88f;
border-radius: 10px;
padding:15px;
}
#main h2{
text-align: center;
}
#frm>textarea{
width:100%;
height:100px;
}
#frm #btnZone{
text-align: center;
}
#frm #btnSubmit{
background-color: #99f;
color:#fff;
}
#frm #btnClose{
background-color: #f99;
}
#main #status{
margin-top:10px;
text-align: center;
background-color: #ccf;
height: 1.5em;
line-height: 1.5em;
}
라디오 버튼이나 체크 박스가 선택될 때마다 현재 상태값을 localStorage에 저장할 것이며 3개의 textarea에 내용을 수정할 때마다 역시 localStorage에 저장할 것입니다.
웹 페이지의 스타일은 크게 무리가 가지 않는 선에서 마무리하였습니다.
2. 이제 본격적으로 자바스크립트 코드를 분석해 보겠습니다.
전체 구조는 "document.frm"을 파라미터로 전달받아 즉시 실행되는 함수를 기본 틀로 잡겠습니다. 이때 "frm"은 <form name="frm'>와 같은 폼태그의 이름입니다.
( (frm)=>{ // (1)
let data=[] // 폼의 값이 실시간으로 저장될 변수
let id="a1234"; // 저장할 때 사용되는 키값
load(); // (2)
frm.addEventListener("input", (ev)=>{ // (3)
store(frm);
})
function store(frm){
}
// 새로고침하거나 다시 브라우저를 실행했을 때
function load(){
}
// 저장소 삭제
let btnDelete = document.querySelector("#btnDelete"); // (4)
btnDelete.addEventListener("click", ()=>{
let yn = confirm("정말 삭제하시겠습니까? ");
if(yn){
localStorage.removeItem(id);
}
})
})(document.frm)
(1) 자바스크립트가 실행되면 즉시 실행함수가 document.frm을 인자값으로 전달해 준 것을 frm으로 받아 실행됩니다. 이때 document.frm은 <form name="frm"/>을 의미합니다.
(2) 웹페이지를 새로 고침 하거나 처음 로딩되었을 때 load() 함수가 호출되어 localStorage에 저장되어 있던 정보를 가져와 웹페이지의 내용을 최근 상태로 재설정하는 함수입니다.
(3) form태그 안에 있는 상태값이 바뀔 때마다 호출되는 이벤트 핸들러가 frm.addEventListener("input"...)입니다. 폼의 내용이 바뀔 때마다 "input"이라는 이벤트가 발생되는 것입니다.
(4) 꼭 필요한 내용은 아니지만 "저장소 삭제" 버튼(btnDelete)이 클릭되면 삭제할지 다시 한번 더 물어본 후 localStorage.removeItem(id)를 통해 저장소의 정보가 삭제됩니다.
3. save(frm) 함수
폼의 내용이 바뀔 때마다 호출되는 함수입니다.
function store(frm){
let formData = new FormData(frm); // (1)
console.log(formData)
let props;
data = [];
for( let[tagName, value] of formData.entries()){ // (2)
let tagType = frm.querySelector(`[name="${tagName}"]`).type; // (3)
props = {
'tagName' : tagName,
'tagType' : tagType,
'value' : value
}
data.push(props); // (4)
}
localStorage.setItem(id, JSON.stringify(data)); // (5)
}
(1) <form/> 태그 안에 있는 값들을 손쉽게 접근하기 위해 formData = new FormData(frm)을 사용하여 객체를 생성해 두고
(2) for문 안에 있는 formData.entries() 함수를 사용하여 선택된 항목들의 태그명과 값을 가져올 수 있습니다.
(3) 태그의 타입을 가져오기 위해 frm 안에서 tagName을 사용하여 해당 태그의 타입을 가져옵니다.
(4) 가져온 3가지의 변숫값을 사용하여 하나의 props를 만들어 data.push(props)를 사용하여 배열에 저장합니다.
(5) localStorage에 저장되는 값의 형태는 문자열이어야 하기 때문에 JSON.stringify(data)를 사용하여 localStorage에 저장합니다. 이때 사용되는 키값은 id값을 사용하였습니다.
4.load() 함수
페이지가 새로고침되거나 브라우저를 새로 로딩했을 때 호출되는 함수입니다.
function load(){
let choiceType = ["radio", "checkbox", "select"]; //(1)
let data = localStorage.getItem(id);//(2)
if(data){
let json = JSON.parse(data); //(3)
json.forEach(d=>{
if(choiceType.includes(d.tagType)){ //(4)
let list = document.querySelectorAll(`[name="${d.tagName}"]`); //(5)
list.forEach(target=>{
if(target.value == d.value) target.checked = true; //(6)
})
}else{
let tag = document.querySelector(`[name="${d.tagName}"]`); //(7)
tag.value = d.value;
}
})
}
}
(1) 폼에 있는 요소들 중 radio, checkbox, select와 같은 요소들은 다른 태그와 달리 여러 개 중에 한 개가 선택되거나 여러 개중에 여러 개가 선택되는 요소입니다. 따라서 이들은 다른 태그와 다른 로직을 통해 접근해야 합니다. 따라서 이를 구분하기 위한 항목으로 배열에 저장해 두었습니다.
(2) localStrage에 저장되어 있는 값들 중 id에 해당하는 값을 가져옵니다. 그러나 가장 처음 페이지가 로딩되거나 저장소가 삭제되어 있다면 localStorage.getItem(id)으로 값을 가져올 수 없습니다. 따라서 조건문을 사용하여 data가 있는 경우만을 처리합니다.
(3) data변수에 값이 있는 경우 이는 문자열로 저장된 상태이기 때문에 다시 객체형으로 바꾸기 위해 JSON.parse(data)를 사용하였습니다.
(4) json.forEach(d=>{... }의 요소는 json에 담긴 데이터를 하나씩 선택하여 변수 d에 대입하고 해당 블록을 수행합니다. 이때 choiceType.includes(d.tagType)는 배열 choiceType에 d.tagType이 포함되어 있는가를 묻는 함수입니다.
(5) json에 저장되어 있는 태그가 radio버튼 중 하나였다고 가정하면 document.querySelectorAll()에 의해 같은 이름을 갖고 있는 모든 radio 버튼들이 선택될 것입니다.
(6) list.forEach()을 하위에 다시 사용하여 radio 버튼들 중 폼태그의 값(target.value)과 데이터의 값(d.value)이 같다면 radio 버튼에 target.checked=true문을 실행시켜 선택된 모습이 되도록 합니다.
(7) 만약 radio, checkbox, select 태그들이 아니라면 태그의 이름으로 태그를 선택한 후 tag.value = d.value를 사용하여 값을 폼 안에 있는 태그에 넣어 줍니다.
코드를 모두 저장하고 브라우저에서 폼 안에 있는 어떤 내용이더라도 수정되면 즉시 localStorage에 저장되고 현재 화면을 새 로고침하여도 선택되거나 입력된 내용들이 그대로 보존되어 있는 것을 알 수 있습니다.
다음으로는 indexedDB를 사용하여 실시간으로 데이터를 저장하고 읽어오는 부분을 살펴보겠습니다.
indexedDB를 사용한 상태값 저장
[indexedDB 요약]
- sql 문장 대신 index키를 사용하여 데이터를 처리함.
- 비동기 방식으로 처리되어 다중 스레드가 동작되는 환경에서도 주변에 영향을 거의 주지 않고 데이터를 처리할 수 있다. 그러나 비동기 방식에서는 데이터의 흐름을 유의해서 처리해야 한다.
- Object 자체를 그대로 저장한다. (localStorage는 문자열만 저장 가능)
1. indexedDB.html 생성
html 파일은 위에서 작성한 localStorage.html을 복사하여 indexedDB.html 파일을 생성합니다. css 파일은 동일한 파일을 사용합니다. html 파일이 복사되었으면 indexedDB.html 파일에서는 자바스크립트 파일을 읽어 오는 부분만이 수정됩니다.
<head>
</head>
2. indexedDB 생성하기
( (frm)=>{
let id="a1234";
let dbName = "myDB";
let table = "exam";
let db; // database object
// database open
let req = indexedDB.open(dbName, 1); //(1)
req.onsuccess=(event)=>{ //(2)
db = event.target.result;
load();
}
req.onerror=(event)=>{ //(3)
console.log("db open error!!!")
}
// db의 구조가 변경되거나 버전이 바뀌었을 때 자동 호출
req.onupgradeneeded=(event)=>{ //(4)
db = event.target.result;
if( !db.objectStoreNames.contains(table)){
db.createObjectStore(table, {keyPath : "id"}); //(5)
}
}
...
})(document.frm)
(1) 지정된 dbName와 버전 1을 사용하여 indexedDB를 오픈합니다.
(2) 정상적으로 indexedDB가 오픈되면 오픈된 데이터베이스 객체를 db에 넣고 load() 함수를 호출합니다.
(3) indexedDB 오픈 시 오류가 발생했다면 처리되는 부분입니다. 이곳에서는 간단히 콘솔창에 메시지를 출력하였습니다.
(4) indexedDB가 처음 오픈되었거나 데이터베이스 구조나 버전이 바뀌었을 때 자동으로 호출되는 이벤트 핸들러입니다. 이곳에서 실제 데이터가 저장될 ObjectStore를 생성하는데 ObjectStore는 RDBMS에서는 테이블이라 볼 수 있습니다.
(5) "exam" store가 생성될 때 데이터를 조작하기 위한 키로 "id" 항목을 지정합니다.
3. load() 함수
화면을 새로고침하거나 브라우저가 처음 열렸을 때 실행되는 함수입니다.
function load(){
let choiceType = ["radio", "checkbox", "select"];
// read data(transaction)
let trans = db.transaction(table, "readonly"); //(1)
let store = trans.objectStore(table); // 데이터 read (2)
let req = store.get(id); // row (3)
let data = {};
req.onsuccess= (event)=>{ //(4)
data = event.target.result; //(5)
if(data){
data.data.forEach(d=>{ //(6)
if(choiceType.includes(d.tagType)){
let list = document.querySelectorAll(`[name="${d.tagName}"]`);
list.forEach(target=>{
if(target.value == d.value) target.checked = true;
})
}else{
let tag = document.querySelector(`[name="${d.tagName}"]`);
tag.value = d.value;
}
})
}
}
}
(1) indexedDB에서 ObjectStore(table)을 사용하려면 항상 transaction을 생성해 주어야 하는데 table로 지정된 ObjectStore를 읽기 전용(readonly)으로 생성해 줍니다.
(2) table로 지정된 저장소(objectStore)를 가져옵니다.
(3) id에 해당하는 한 행을 가져와 rep에 대입합니다. 이는 localStorage에서 보았던 data배열에 저장되어 있는 props구조라 생각하시면 될듯합니다.
(4) id에 해당하는 데이터를 정상적으로 가져왔을 때 자동으로 핸들링됩니다.
(5) 이벤트 정보에 담긴 데이터를 data변수에 대입합니다. 이때 data에 저장되는 데이터 구조는 아래와 같습니다.
data = {
'id' : 'a1234',
'data' : [... ]
}
(6) data.data에 담긴 자료를 forEach문으로 반복처리 하고 있는데 이 부분은 localStorage.js에서와 동일한 작업이므로 설명은 생략하도록 하겠습니다.
4. store() 함수
frm.addEventListener("input",...)에 의해서 호출되는 함수입니다. 이곳에서 <form/> 안에 있는 데이터가 수정될 때 마다 실시간으로 indexedDB에 저장됩니다.
frm.addEventListener("input", store);
function store(){
let formData = new FormData(frm); //(1)
let data=[];
for( let[tagName, value] of formData.entries()){ //(2)
let tagType = frm.querySelector(`[name="${tagName}"]`).type; //(3)
props = { //(4)
'tagName' : tagName,
'tagType' : tagType,
'value' : value
}
data.push(props);
}
console.log(data);
let trans = db.transaction(table, "readwrite"); //(5)
let store = trans.objectStore(table); //(6)
let storeData = { //(7)
"id" : id,
"data" : data
}
let req = store.put(storeData); //(8)
req.onsuccess=(event)=>{
console.log("데이터가 저장되었습니다.");
}
}
(1) <form/>안에 있는 선택된 항목들만을 사용하여 formData객체로 만듭니다.
(2) formData안에 있는 항목들을 하나씩 가져와 tagName, 과 value값을 갖고 for문을 실행합니다.
(3) formData에는 태그의 유형이 저장되지 않기 때문에 tagName을 사용하여 태그의 타입을 tagType에 대입합니다. 라디오 버튼을 예로 들면, 여러 개의 라이오 버튼 중 선택된 항목 하나만이 formData에 저장되므로 tagName만을 사용해도 태그의 타입을 가져올 수 있습니다.
(4) 태그 각각의 요소들을 사용하여 하나의 props 객체를 생성하여 data.push(props)을 사용하여 배열에 저장합니다.
(5) table명으로 지정된 objectStore를 읽고 쓰기 용으로 transaction을 생성합니다.
(6) table명으로 지정된 objectStore을 가져와 store객체로 생성합니다.\
(7) 저장할 데이터 형식을 만듭니다. 여기서 "id"는 데이터를 구분하거나 찾기 위한 index key입니다. ObjectStore를 생성할 때 두 번째 파라미터인 {keyPath : "id"}와 연관이 있습니다.
(8) 데이터를 저장합니다. 만약 동일한 "id"값이 존재한다면 기존 데이터가 업데이트됩니다.
이것으로 localStorage와 indexedDB를 사용하여 현재 상태값을 실시간으로 저장하고 적용하는 기능을 살펴보았습니다. 간단하고 쉽게 처리할 수 있는 방법은 localStorage를 사용하는 방법이지만, 한 페이지 내에서 여러 개의 스레드 작업이 진행되고 있거나, 대량의 데이터를 실시간으로 저장하려면 localStorage 보다는 indexedDB를 사용하는 것이 좋은 선택지가 되지 않을까 생각됩니다. 다만, 코드 내용이 다소 복잡해지고 비동기적 처리를 해야 한다는 다소 부담감이 있는 것은 사실입니다.
개발할 때 본 여행지에서 경험한 경험치가 좋은 영감이 되기를 바랍니다.
저는 다시 다른 여행지에서 찾아뵙겠습니다. 감사합니다.
전체 코드 : https://github.com/hiparkwg/localStorageVSindexedDB.git
영상 : https://youtu.be/dBus3ak-agM
'BOOKS > 수월한 자바스크립트' 카테고리의 다른 글
NTSC에서 개발된 계산식을 사용한 바탕색 대비 가독성 있는 폰트색 만들기 (0) | 2025.03.23 |
---|---|
<template/>를 사용한 동적 페이지 만들기 (0) | 2025.03.22 |
chart.js를 사용한 데이터 시각화하기 #3/3 (4) | 2024.12.27 |
chart.js를 사용한 데이터 시각화하기 #2/3 (0) | 2024.12.27 |
chart.js를 사용한 데이터 시각화하기 #1/3 (4) | 2024.12.26 |