window.localStorage
이전에 배웠듯 전역 객체인 window는 생략 가능하다. localStorage 읽기 전용 속성을 사용하면 Document 출처의 Storage 객체에 접근할 수 있다. 저장한 데이터는 브라우저 세션 간에 공유된다. localStorage는 sessionStorage와 비슷하지만, localStorage의 데이터는 만료되지 않고 sessionStorage의 데이터는 페이지 세션이 끝날 때, 즉 페이지를 닫을 때 사라지는 점이 다르다.
myStorage = window.localStorage;
// 현재 출처의 로컬 저장 공간에 접근할 수 있는 Storage 객체.
현재 도메인의 로컬 Storage 객체에 접근한 후,
localStorage 항목 하나 추가
(로컬 스토리지에서 바라봤을 때 항목 추가를 create라고 바라봤다)
localStorage.setItem('myCat', 'Tom');
위에서 추가한 localStorage 항목 접근(읽기)
const cat = localStorage.getItem('myCat');
localStorage 항목 제거
localStorage.removeItem('myCat');
localStorage 항목의 전체 제거
localStorage.clear();
localStorage의 키를 얻거나 설정할 때, 아래처럼 일반 객체와 유사한 방법을 사용할 수 있다.
// 키 설정하기
localStorage.test = 2;
// 키 얻기
alert( localStorage.test ); // 2
// 키 삭제하기
delete localStorage.test;
하위 호환성 때문에 아직 이런 방법이 지원되기는 하지만, 다음과 같은 이유로 추천하지 않는다.
let key = 'length';
localStorage[key] = 5; // TypeError: Cannot assign to read only property 'length'...
localStorage는 '키(Key)’를 사용해 값(Value)을 얻고(get), 설정(set)하고, 삭제(delete)할 수 있게 해준다. 그렇다면 키나 값 전체는 어떻게 얻을 수 있을까?
아쉽게도 로컬스토리지 객체는 iterable 객체가 아니다.
iterable object
반복 가능한(iterable, 이터러블) 객체는 배열을 일반화한 객체이다. 이터러블 이라는 개념을 사용하면 어떤 객체에든 for..of 반복문을 적용할 수 있다. 배열은 대표적인 이터러블이다. 배열 외에도 다수의 내장 객체가 반복 가능하다. 문자열 역시 이터러블의 예이다.
하지만 배열처럼 다루면 전체 키-값을 얻을 수 있다.
for (let i=0; i<localStorage.length; i++) {
let key = localStorage.key(i);
alert(`${key}: ${localStorage.getItem(key)}`);
}
일반 객체를 다룰 때처럼 for key in localStorage 반복문을 사용해도 전체 키-값을 얻을 수 있다. 하지만 이 방법을 사용하면 필요하지 않은 내장 필드까지 출력된다.
// 좋지 않은 방법
for(let key in localStorage) {
alert(key);
// getItem, setItem 같은 내장 필드까지 출력됩니다.
// proxyConsoleLog.js:12 _ym32184394_lsid,
// _ym_uid, length, clear,
// getItem, key, removeItem, setItem ~~~
}
for key in localStorage 반복문을 사용하려면 hasOwnProperty를 이용해 프로토타입에서 상속받은 필드를 골라내야 한다.
for(let key in localStorage) {
if (!localStorage.hasOwnProperty(key)) {
continue; // setItem, getItem 등의 키를 건너뜁니다.
}
alert(`${key}: ${localStorage.getItem(key)}`);
}
아니면 아래처럼 Object.keys로 '자기 자신’의 키를 받아온 다음 순회하는 방법을 사용할 수도 있다.
let keys = Object.keys(localStorage);
for(let key of keys) {
alert(`${key}: ${localStorage.getItem(key)}`);
}
Object.keys는 해당 객체에서 정의한 키만 반환하고 프로토타입에서 상속받은 키는 무시하기 때문이다.
localStorage의 키와 값은 반드시 문자열이어야 한다. 숫자나 객체 등 다른 자료형을 사용하게 되면 문자열로 자동 변환된다.
sessionStorage.user = {name: "John"};
alert(sessionStorage.user); // [object Object]
JSON을 사용하면 객체를 쓸 수 있긴 합니다.
sessionStorage.user = JSON.stringify({name: "John"});
let user = JSON.parse( sessionStorage.user );
alert( user.name ); // John
디버깅 등의 목적으로 스토리지 객체 전체를 문자열로 변환하는 것도 가능하다.
<!-- HTML code -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width-device-width", initial-scale="1.0">
<link rel="stylesheet" href="style.css">
<title>#4</title>
</head>
</html>
<body>
<form class="hidden" id="login-form">
<input
required
maxlength = "15"
type="text"
placeholder="what is your name?"
/>
<button>Log in</button>
</form>
<h1 class="hidden" id="greeting"></h1>
<script src="app.js"></script>
</body>
// JS code
const loginForm = document.querySelector("#login-form");
const loginInput = document.querySelector("#login-form input");
const greeting = document.querySelector("#greeting");
const HIDDEN_CLASSNAME = "hidden";
const USERNAME_KEY = "username";
function onLoginSubmit(event) {
event.preventDefault();
// 브라우저의 기본 동작을 막아준다. => 페이지 새로고침을 막아준다.
const username = loginInput.value;
localStorage.setItem(USERNAME_KEY, username);
paintGreetings(username);
}
function paintGreetings(username) {
loginForm.classList.add(HIDDEN_CLASSNAME);
greeting.innerText = `Hello ${username}`;
greeting.classList.remove(HIDDEN_CLASSNAME);
}
const saveUsername = localStorage.getItem(USERNAME_KEY);
if (saveUsername === null){
loginForm.classList.remove(HIDDEN_CLASSNAME);
loginForm.addEventListener("submit", onLoginSubmit);
} else {
paintGreetings(saveUsername);
// 사실 saveUsername은 로컬 스토리지에 저장되어 있으니
// 굳이 인자로 전해줄 필요가 없음
// 근데 그러면 로컬 스토리지를 두 번 열어봐야겠지
}