localStorage를 이용한 메모장

이지·2023년 4월 28일
0
post-thumbnail

로컬스토리지를 이용해서 새로고침해도 메모된 내용이 사라지지 않도록 해보자.

일단 내가 만들고 싶은 메모장은
1. 버튼을 누르면 메모를 입력할 수 있는 입력창이 화면에 출력
2. 입력창을 이용해 제목과 내용을 입력한 후 저장
3. 로컬스토리지에 저장됨
4. 저장된 데이터가 메모지에 나타남
5. 삭제버튼을 누르면 메모지 삭제
(+ 추가 기능 : 랜덤으로 메모지의 색상 바뀌게 하기)


HTML

<main>
  <h1 onclick="addMemo()">MEMO</h1>
  <div class="container"></div>
</main>
  • 클릭하면 메모입력폼을 출력하기 위한 h1태그 MEMO
  • 컨테이너를 미리 만들어놓고 자바스크립트를 이용해 안의 요소들을 추가했다.

JavaScript

let allMemo = JSON.parse(localStorage.getItem("allMemo"));
const container = document.querySelector(".container");

allMemo = allMemo ?? [];
render();

메모 입력폼 추가를 위한 addMemo()

function addMemo() {
        const memo = document.createElement("article");
        const form = document.createElement("form");
        container.appendChild(memo);
        memo.appendChild(form);

        const title = document.createElement("input");
        const content = document.createElement("textarea");
        const saveBtn = document.createElement("button");

        memo.classList.add("memo");
        memo.setAttribute("id", "memo-input");

        form.classList.add("form-memo");

        title.setAttribute("type", "text");
        title.setAttribute("id", "title-input");
        title.setAttribute("placeholder", "제목을 입력하세요");

        content.setAttribute("id", "content-input");

        saveBtn.classList.add("btn-save");
        saveBtn.setAttribute("type", "text");
        saveBtn.setAttribute("onclick", "saveMemo()");
        saveBtn.textContent = "SAVE";

        form.append(title, content, saveBtn);
}

메모 내용 로컬스토리지에 저장하는 saveMemo()

function saveMemo() {
        const title = document.getElementById("title-input").value;
        const content = document.getElementById("content-input").value;
        allMemo.push({ title, content, len: allMemo.length});

        localStorage.setItem("allMemo", JSON.stringify(allMemo));
        render();
        }

로컬스토리지의 데이터를 불러오는 render()

function render() {
        for (let item = 0; item < allMemo.length; item++) {
            const memo = document.createElement("article");
            memo.classList.add("memo");
            container.append(memo);

            const memos = document.querySelectorAll(".memo");

            for (let i = 0; i < memos.length; i++) {
                //allMemo와 memos의 인덱스가 같은 경우에만 메모지 출력
                if (i === item) {
                    const saveTitle = document.createElement("h2");
                    const saveContent = document.createElement("p");
                    const deleteBtn = document.createElement("button");
                    saveTitle.textContent = allMemo[item].title;
                    saveTitle.classList.add("title-memo");

                    saveContent.innerText = allMemo[item].content;
                    saveContent.classList.add("content-memo");

                    deleteBtn.classList.add("btn-delete");
                    deleteBtn.setAttribute("id", allMemo[item].len);
                    deleteBtn.setAttribute("onclick", "remove()");
                    deleteBtn.textContent = "DELETE";

                    memo.classList.add(allMemo[item].color);
                    memo.appendChild(saveTitle);
                    memo.appendChild(saveContent);
                    memo.appendChild(deleteBtn);

                    //로컬에 저장된 전체 개수보다 메모지의 수가 많으면 필요없는 메모지는 삭제해주기
                    if (memos.length > allMemo.length) {
                        for (let i = allMemo.length; i < memos.length; i++) {
                            container.removeChild(memos[i])
                        }
                    }
                }
            }
        }
  }
  • render() 함수에서 어려웠던 점이 많았는데 그 중 하나는 로컬스토리지에 저장된 메모지를 어떻게 불러와야될까였다. 메모를 입력하는 폼이 계속 화면에 출력되고 있는 상태가 아니고 버튼을 눌러야 출력되는 구조라서 render()함수가 실행되면 메모지를 만들고 해당 메모지에 로컬에 저장된 데이터를 불러와줬다.

로컬스토리지에서 메모를 불러오는것은 해결했다. 그러나...다른문제가 생겼다 ^^

  • 문제 : 삭제를 누르면 삭제한 메모지만 사라져야하는데 남은 메모지가 뒤에 또 생겨났다 ㅠㅠㅠ

    -> 이 문제는 내가 로컬 스토리지에서 메모지를 불러오기 위해 render()에서 메모지를 만들어 준 것이 원인이었다. 삭제를 하면 remove함수의 마지막에 render()가 실행되기 때문에 또 다시 로컬에서 저장된 메모를 불러오게 된 것.. 이 문제를 해결하려면 렌더함수를 다시 짜거나 입력폼이 아예 고정적으로 화면에 출력된 상태를 유지하도록 만들어야할 것 같다고 생각했다. 코드를 다시 짜보려했지만 실패,, 그리고 입력폼이 고정적으로 있으면 예쁘지 않아서 계속 고민을 해보았다. 결국 방법을 찾긴 찾았다!(약간 야매 같긴하지만..) 늘어난 메모지를 삭제해주는 것! 아래의 코드를 render 함수의 마지막에 추가해주었다.
    if (memos.length > allMemo.length) {
        for (let i = allMemo.length; i < memos.length; i++) {
            container.removeChild(memos[i])
        }
    }

메모 삭제하는 remove()

function remove() {
      const idx = allMemo.find(item => item.len == event.srcElement.id);
      if (idx) {
          allMemo.splice(allMemo.findIndex(item => item.len == idx.len), 1);
      }
      localStorage.setItem("allMemo", JSON.stringify(allMemo));
      render();
      location.reload();
}
  • 삭제에서도 문제가 있었는데 5개의 메모가 있다고 가정하면 2번째 메모를 삭제하면 2번째 메모가 삭제되는것이 아니라 마지막 메모가 삭제되었다.. 왜지? 진짜 5번째 메모가 삭제된걸까? 새로고침을 해보면 2번째 메모가 삭제된게 맞긴했다. 코드를 여러방면으로 고쳐봤지만 하나도 도움이 되지않았고 나는 결국 새로고침을 하는 함수가 있는지 찾아보았다. location.reload() 해당 코드를 사용하면 새로고침이 된다. 사실 새로고침하는건 좋지 않은 선택인 것 같긴한데.. 나중에 해당 메모장 코드를 리팩토링할 계획이라서 그때 다시 생각해보도록 하기로...😅

메모지의 색상을 랜덤으로 바뀌게 하는 방법

let colorMemo = ["color1", "color2", "color3", "color4"];
let randomColor = Math.floor(Math.random() * colorMemo.length);
allMemo.push({ title, content, len: allMemo.length, color: colorMemo[randomColor] });

-> 해당 코드를 render() 함수안에 추가했다.
-> colorMemo에 저장된 요소들은 css에서 따로 .color1, .color2, ...에 색상을 지정해주었다.
-> 처음엔 saveMemo() 함수가 아닌 render() 함수에서 Math.random 함수를 사용해서 메모지의 색상을 랜덤으로 바뀌게 했는데 그랬더니 메모를 저장하거나 삭제, 새로고침 하는 모든 경우에 색이 변경되었다. 내가 생각한 건 이게 아닌데..^^;; 그냥 이대로 냅둘까하다가 로컬을 이용하면 되지 않을까라는 생각이 들어서 saveMemo 함수에서 allMemo에 color: colorMemo[randomColor] 로 저장해주었다.
-> render 함수에서 memo.classList.add(allMemo[item].color); 클래스 추가해주는 것도 잊으면 안된다!


-> 완성된 메모장이다!
-> 내용이 너무 길어지면 스크롤로 위아래로 움직일 수 있도록 해주었다.
-> 입력폼은 상단의 MEMO를 누르면 생긴다
-> 디자인은 다이어리 꾸미는 것을 좋아해서 자주 문구상품들을 구경하는데 내 취향인 심플한 디자인을 참고해서 만들었다.

간단한 메모장이지만 많은 고민과 매번 생겨나는 에러들로 인해 고통을 느낄 수 있게 해준.. 밤늦게까지 고민도 해보기도 하고, 자려고 누웠는데 갑자기 이렇게 하면 어떨까? 라는 생각이 들어서 자려다 말고 노트에 적어놓기도 했다 😂 또 샤워하다가도 메모장 생각이 나서 로직을 머릿속으로 다시 짜보기도 했다 ㅋㅋㅋ 역시 직접 만드는게 실력 향상에 좋은 방법인 것 같다. 코드가 만족스럽진 않지만 일단 돌아가게 만든거에 의의를..! 나중에 꼭 다시 리팩토링하고 글 올려야지!!

0개의 댓글