본문 바로가기
Programming/JavaScript

[JS] Local Storage

by 공부합시다홍아 2024. 6. 13.
 

[JS] D-DAY 카운트 구현

[JavaScript] 날짜를 생성하는 New DateDate 생성자는 시간의 특정 지점을 나타내는 Date 객체를 플랫폼에 종속되지 않는 형태로 생성한다.Date 객체는 1970년 1월 1일 UTC(국제표준시) 자정으로부터 지난

hong-study.tistory.com

이전 포스팅에서는 JS를 이용해 D-day 카운터를 구현을 해보았다. 하지만 입력한 D-Day 화면을 새로 고침할 때마다 
내가 입력한 모든 값과 카운팅되는 값들이 사라지는 현상이 있는데, 이 문제를 Local Storage로 해결해보고자, 오늘은 Local Storage는 무엇이고 언제 사용하는 지에 대해 공부하려고 한다.


웹 브라우저에서 어떠한 로직에 활용되는 데이터를 유지시키고자 한다면, 해당 데이터를 저장할 공간이 필요하다.
웹 브라우저는 Web Storage라는 저장소를 제공하는데, Session StorageLocalStorage를 활용할 수 있다.

두 개의 스토리지는 모두 로컬 환경에 데이터를 저장한다는 특징을 공통점으로 가진다. 그리고 Key-Value 형태로 저장이 된다. 이때, Key-Value는 모두 문자열 형태로 변환되어 저장이 되어야 한다. 


차이점

Session Storage

데이터를 세션 단위로 저장한다. 세션은 사용자가 페이지에 접속하는 순간부터 접속이 끊어지는 순간까지를 이야기 한다.
즉, 해당 페이지와 접속이 끊어지거나 브라우저가 종료되면 세션 저장소의 데이터를 영구히 삭제하는 휘발성 메모리이다.

Local Storage

로컬 스토리지는 데이터를 도메인 단위로 저장하고, 저장된 데이터가 도메인이 같다면 path가 다르더라도 서로 데이터를 공유할 수 있다. 또한 로컬 스토리지는 비휘발성 메모리이기 때문에 저장된 데이터가 브라우저, PC를 종료하더라도 그대로 남아 있을 수 있다. 반영구적인 데이터이기 때문에 직접 스토리지 데이터를 삭제할 때까지 존재한다.
이러한 특징 때문에 페이지를 재방문 했을 때도 남아있어야 하는 사용자 개별 설정 등의 데이터를 저장할 수 있다.

session Stroage
● key-value 형태로 저장
● 로컬환경에 데이터 저장
● 문자열 형태로 데이터 저장
● 세션 단위로 데이터 저장
● 브라우저, 탭을 종료 시 영구 삭제

Local Storage
● key-value 형태로 저장
● 로컬 환경에 데이터 저장
● 문자열 형태로 데이터 저장
● 도메인 단위로 데이터 저장
● 브라우저, PC를 종료해도 존재

Session =>
사용자가 브라우저를 통해 페이지에 접속한 시점부터, 해당 접속을 종료하는 시점까지를 의미하는 단위

Domain =>
url의 프로토콜(http://) 바로 뒷부분에 따라오는 문자를 통해 도메인(localhost)을 확인할 수 있다.
"<http://localhost:5500/user/login>"
"<http://localhost:5500/post>"
// localhost라는 공통된 도메인, 서로 다른 path(/user/login, /post)​

Local Storage 접근 방법

로컬 스토리지의 사용 방법은 매우 간단해 아래와 같이 입력해주면 된다.

localStorage.setItem('data-key', 'data-value')

아래는 내가 사용한 코드이다.

const starter = function() {
    const targetDateInput = dateFormMaker();
    localStorage.setItem("saved-date", targetDateInput);
    container.style.display = "flex";
    msgContainer.style.display = "none";
    setClearInterval();
    counterMaker(targetDateInput);
    
    const intervalId = setInterval(()=> {counterMaker(targetDateInput);}, 1000 ); // 반환값이 해당 Interval의 아이디
    intervalArr.push(intervalId);
}

이전 포스팅을 확인하면, starter가 어떤 동작을 해주는 함수인지 알 수 있는 건데, 스토리지 내부에 Input 태그에서 입력한 값을 저장시켜주는 것이다. 이렇게 입력하고 실행을 시켜서 개발자 도구를 확인하면 아래와 같이 로컬 스토리지 내부에
Key-Value 형태로 저장되어 있는 것을 확인할 수 있다.

하지만 이렇게 저장했다해도, 데이터를 그대로 유지하기는 힘들기 때문에 아래와 같이 활용해야 한다.
즉, 데이터를 가져와야 하는 것이다.

데이터를 가져오는 방법은 데이터를 저장하는 방법처럼 한 줄만 추가해주면 된다.

localStorage.getItem("saved-date");

바로 getItem을 이용해서 값을 얻어올 수 있다.

하지만 이 구문을 setItem 아래에 위치시키게 되면 HTML과 JS 화면 랜더링시, 동작을 안하시 때문에 나는 JS 구문 최상단에 위치 시켜줬다. 이때 아래 처럼 변수를 지정해서 사용을 하였다.

const savedDate = localStorage.getItem("saved-date");

이렇게 하면 얻어온 값을 변수에 저장할 수가 있는데, 이렇게 해도 아직 한가지 유효성 검사를 할 필요가 있다.
그건 카운터 시작에 대한 함수가 실행할 때 로컬 스토리지에 값이 일 때의 경우와
저장된 값이 truthly일 경우에 실행하는 구문을 추가해줘야 한다.

const starter = function(targetDateInput) {

    if(!targetDateInput){
        targetDateInput = dateFormMaker();
    }

    localStorage.setItem("saved-date", targetDateInput);
    
    container.style.display = "flex";
    msgContainer.style.display = "none";
    setClearInterval();
    counterMaker(targetDateInput);
    
    const intervalId = setInterval(()=> {counterMaker(targetDateInput);}, 1000 ); // 반환값이 해당 Interval의 아이디
    intervalArr.push(intervalId);
}

이 과정에서 나는 기존의 const targetDateInput을 starter() 함수의 매개변수로 변경시키고, 그 값이 undefined가 아닐 때, 
"일", "시간", "분", "초"를 계산하는 함수가 동작하도록 하였다.

그리고 하단에 로컬스토리지에 저장된 값을 제대로 들어있는지 확인을 먼저 한 다음에 starter() 함수가 동작해야 하기 때문에 조건문을 통해 로컬 스토리지 내 key-value 값 존재 유무에 대한 조건문을 적어줬다.

if(savedDate){
    starter(savedDate);
}else{
    container.style.display = "none";
    msgContainer.innerHTML = "<h3>D-day를 입력해주세요.</h3>";
}

위와 같이 일련의 과정을 거치면 새로고침을 해도, 기존의 카운팅되고 있는 수가 사라지지 않고 계산 화면에 표출되게 된다. 하지만 이렇게만 구성하였을 경우, 큰 문제가 있다. 그것은 타이머 초기화 기능을 사용해, 데이터를 날려도 이전의 로컬 스토리지에 저장된 값에 의해 카운팅되고 있는 결과가 계속 나오는 현상때문이다.
즉, 로컬 스토리지의 Key-Value 값의 업데이트가 필요하다는 걸 알 수 있다.

일단, 데이터 초기화를 할 때 이전 포스팅에서 clearInterval() 함수를 이용해서, 배열에 담긴 Interval 아이디를 삭제해준다는 것을 알 수 있는데, SetClearInterval() 함수에 로컬스토리지 내의 저장된 "key" 값을 삭제할 수 있는 로직을 아래와 같이 작성한다.

const setClearInterval = function(){
    localStorage.removeItem("saved-date");
    for(var i = 0; i<intervalArr.length; i++){
        clearInterval(intervalArr[i]);
    }
}

localstorage.removeItem() 함수를 이용해 내부 파라미터에 "키 값"을 입력하여 제거할 수가 있다.
이때, 기존과 같이 구성한 뒤 실행을 시켰을 때, 등록과 동시에 키값이 사라져 새로고침을 해도 다시 카운팅이 사라지는 것을 확인했다. 이 문제를 해결할려고 랜더링이 어떻게 이루어지는 지 다시 확인했던 거 같다.

그래서 아래와 같이 로컬스토리지에 savedData를 저장하는 함수를 counterMaker(), 즉 카운팅 계산이 들어가는 곳으로 배치를 시켜준 이후에 동작을 하니 정상적으로 실행되었다.

const counterMaker = function (data) {
    if(data !== savedDate){
        localStorage.setItem("saved-date", data);
    }
    
    const nowDate = new Date();
    const targetDate = new Date(data).setHours(0,0,0,0);
    const remaining = (targetDate - nowDate) / 1000;

    // reamining이 0이 된다면 현재 시간이 목표 시간에 도달
    if(remaining == 0){
        container.style.display = "none";
        msgContainer.innerHTML = "<h3>타이머가 종료되었습니다.</h3>"
        msgContainer.style.display = "flex";
        resetTimer();
        return;
    }else if(isNaN(remaining)){
        container.style.display = "none";
        msgContainer.innerHTML = "<h3>유효한 시간대가 아닙니다.</h3>"
        msgContainer.style.display = "flex";
        resetTimer();
        return;
    }else if(remaining < 0){
        container.style.display = "none";
        msgContainer.innerHTML = "<h3>날짜가 지나 확인이 불가합니다. 다시 입력해주세요</h3>"
        msgContainer.style.display = "flex";
        resetTimer();
        return;
    }

    const remainingObj = {
        remainingDate: Math.floor(remaining / 3600 / 24),
        remainingHours: Math.floor(remaining / 3600) % 24,
        remainingMin: Math.floor(remaining / 60) % 60,
        remainingSec: Math.floor(remaining) % 60,
    };

전체 소스 코드

const msgContainer = document.querySelector("#d-day-message");
const container = document.querySelector("#d-day-container");
const savedDate = localStorage.getItem("saved-date");
const intervalArr = [];

container.style.display = "none";
msgContainer.innerHTML = "<h3>D-day를 입력해주세요.</h3>";



const dateFormMaker = function () {
    const inputYear = document.querySelector("#target-year-input").value;
    const inputMonth = document.querySelector("#target-month-input").value;
    const inputDate = document.querySelector("#target-date-input").value;

    const dateFormat = `${inputYear}-${inputMonth}-${inputDate}`;

    return dateFormat;
};

const counterMaker = function (data) {
    if(data !== savedDate){
        localStorage.setItem("saved-date", data);
    }
    
    const nowDate = new Date();
    const targetDate = new Date(data).setHours(0,0,0,0);
    const remaining = (targetDate - nowDate) / 1000;

    // reamining이 0이 된다면 현재 시간이 목표 시간에 도달
    if(remaining == 0){
        container.style.display = "none";
        msgContainer.innerHTML = "<h3>타이머가 종료되었습니다.</h3>"
        msgContainer.style.display = "flex";
        resetTimer();
        return;
    }else if(isNaN(remaining)){
        container.style.display = "none";
        msgContainer.innerHTML = "<h3>유효한 시간대가 아닙니다.</h3>"
        msgContainer.style.display = "flex";
        resetTimer();
        return;
    }else if(remaining < 0){
        container.style.display = "none";
        msgContainer.innerHTML = "<h3>날짜가 지나 확인이 불가합니다. 다시 입력해주세요</h3>"
        msgContainer.style.display = "flex";
        resetTimer();
        return;
    }

    const remainingObj = {
        remainingDate: Math.floor(remaining / 3600 / 24),
        remainingHours: Math.floor(remaining / 3600) % 24,
        remainingMin: Math.floor(remaining / 60) % 60,
        remainingSec: Math.floor(remaining) % 60,
    };


    const documentArr = ['days', 'hours', 'min', 'sec'];
    const timeKeys = Object.keys(remainingObj);

    const format = function(time) {
        if(time < 10){
            return "0" + time;
        }else{
            return time;
        }
    }

    var i = 0;
    for(var tag of documentArr){
        const remainingTime = format(remainingObj[timeKeys[i]]);
        document.getElementById(tag).textContent = remainingTime;
        i++;
    }

};

const starter = function(targetDateInput) {

    if(!targetDateInput){
        targetDateInput = dateFormMaker();
    }

    container.style.display = "flex";
    msgContainer.style.display = "none";
    setClearInterval();
    counterMaker(targetDateInput);
    
    const intervalId = setInterval(()=> {counterMaker(targetDateInput);}, 1000 ); // 반환값이 해당 Interval의 아이디
    intervalArr.push(intervalId);
}

const setClearInterval = function(){
    localStorage.removeItem("saved-date");
    for(var i = 0; i<intervalArr.length; i++){
        clearInterval(intervalArr[i]);
    }
}

const resetTimer = function(){
    
    container.style.display = "none";
    msgContainer.innerHTML = "<h3>D-day를 입력해주세요.</h3>";
    msgContainer.style.display = "flex";
    setClearInterval();
}

if(savedDate){
    starter(savedDate);
}else{
    container.style.display = "none";
    msgContainer.innerHTML = "<h3>D-day를 입력해주세요.</h3>";
}
728x90

'Programming > JavaScript' 카테고리의 다른 글

[JS] Scope(스코프)  (1) 2024.06.14
[JS] To do List 구현  (0) 2024.06.13
[JS] D-DAY 카운트 구현  (0) 2024.06.13
[JavaScript] 날짜를 생성하는 New Date  (0) 2024.06.11
[JavsScript] API 활용  (0) 2024.03.07