안녕하세요, 코딩하는곰입니다! 🐾 오늘은 JavaScript의 강력한 브라우저 저장소 API인 localStorage를 활용해, 브라우저를 닫아도 데이터가 사라지지 않는 나만의 메모장 웹 애플리케이션을 만들어보려고 합니다. 프론트엔드 개발을 시작하신 지 얼마 안 되신 분들께는 localStorage가 무엇인지, 어떻게 사용하는지 궁금하실 텐데요. 이번 포스팅을 통해 이 기술의 개념을 명확히 이해하고, 실제로 동작하는 메모장을 직접 코딩해보는 시간을 가져보겠습니다. 간단해 보이지만, 웹 앱의 기본기가 되는 데이터 관리의 핵심을 배울 수 있는 아주 좋은 실습 프로젝트입니다. 지금 바로 코드 에디터를 열고 함께 따라와 주세요!
localStorage는 웹 브라우저에 내장된 클라이언트 사이드 스토리지 솔루션입니다. 사용자의 로컬 머신(브라우저)에 키-값(key-value) 쌍의 형태로 데이터를 저장할 수 있게 해주죠. 가장 큰 장점은 브라우저 세션이 끝나도(탭이나 창을 닫아도) 데이터가 유지된다는 점입니다. 이는 임시 저장에 사용되는 sessionStorage와의 결정적인 차이점이에요.
그렇다면 쿠키(Cookie)와는 무엇이 다를까요? 쿠키도 데이터를 저장하지만, 매 HTTP 요청마다 서버로 자동 전송되며 용량도 매우 제한적(약 4KB)입니다. 반면 localStorage는 일반적으로 도메인당 최소 5MB 이상의 공간을 제공하며, 서버로 자동 전송되지 않아 보안性和 효율性이 더 뛰어납니다. 따라서 사용자의 설정, 폼 데이터 자동 저장, 간단한 애플리케이션 상태 등 오프라인에서도 유지되어야 할 데이터를 저장하는 데 안성맞춤입니다.
localStorage의 API는 놀라울 정도로 직관적이고 간단합니다. 주로 사용하는 메서드는 다음과 같습니다.
setItem(key, value): 데이터 저장getItem(key): 데이터 조회removeItem(key): 특정 데이터 삭제clear(): 모든 데이터 삭제key(index): 인덱스로 키 이름 조회length: 저장된 아이템의 개수
여기서 중요한 점은 value로 문자열만 저장할 수 있다는 것입니다. 객체나 배열을 저장하려면 JSON.stringify()를 사용해 문자열로 변환(직렬화)해야 하고, 꺼내올 때는 JSON.parse()를 사용해 원래 자료형으로 변환(역직렬화)해야 합니다. 이 과정이 이번 메모장 프로젝트의 핵심 로직이 될 거예요.
📱 앱 개발에 도전하고 싶다면, (자바 내부 클래스 완벽 가이드) 정적/비정적 내부 클래스의 모든 것를 참고해보세요.
먼저 우리가 만들 메모장의 기능을 정의해보죠.
<!DOCTYPE html><html lang="ko"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>곰의 LocalStorage 메모장</title><style>* { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Malgun Gothic', sans-serif; }body { background-color: #f0f2f5; padding: 20px; color: #333; }.container { max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 15px; box-shadow: 0 5px 15px rgba(0,0,0,0.08); }h1 { color: #2c3e50; margin-bottom: 20px; text-align: center; border-bottom: 3px solid #3498db; padding-bottom: 10px; }.memo-input-section { margin-bottom: 30px; }textarea { width: 100%; height: 150px; padding: 15px; border: 2px solid #ddd; border-radius: 8px; font-size: 16px; resize: vertical; transition: border 0.3s; }textarea:focus { outline: none; border-color: #3498db; }.button-group { margin-top: 15px; display: flex; gap: 10px; flex-wrap: wrap; }button { padding: 12px 25px; border: none; border-radius: 6px; cursor: pointer; font-size: 16px; font-weight: bold; transition: all 0.2s ease; }#saveBtn { background-color: #2ecc71; color: white; }#saveBtn:hover { background-color: #27ae60; }#clearBtn { background-color: #e74c3c; color: white; }#clearBtn:hover { background-color: #c0392b; }#newBtn { background-color: #3498db; color: white; }#newBtn:hover { background-color: #2980b9; }.memo-list-section h2 { color: #2c3e50; margin-bottom: 15px; padding-bottom: 5px; border-bottom: 2px solid #eee; }#memoList { list-style: none; }.memo-item { background: #f8f9fa; margin-bottom: 10px; padding: 15px; border-left: 5px solid #3498db; border-radius: 5px; display: flex; justify-content: space-between; align-items: center; cursor: pointer; transition: background 0.2s; }.memo-item:hover { background: #e9ecef; }.memo-content { flex-grow: 1; }.memo-date { font-size: 0.85em; color: #7f8c8d; margin-top: 5px; }.delete-btn { background-color: #95a5a6; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; font-size: 0.9em; }.delete-btn:hover { background-color: #7f8c8d; }</style></head><body><div class="container"><h1>🐾 코딩하는곰의 LocalStorage 메모장</h1><section class="memo-input-section"><textarea id="memoInput" placeholder="여기에 메모를 입력하세요..."></textarea><div class="button-group"><button id="saveBtn">💾 메모 저장</button><button id="newBtn">📄 새 메모</button><button id="clearBtn">🗑️ 전체 삭제</button></div></section><section class="memo-list-section"><h2>📋 저장된 메모 목록</h2><ul id="memoList"></ul></section></div><script src="memo.js"></script></body></html>
홍보용 전단이나 SNS 콘텐츠에 맞춤형 QR 코드를 넣고 싶을 때는 컬러 커스터마이징이 가능한 QR 생성기를 사용해보세요.
이제 본격적으로 localStorage를 조작하고 DOM을 제어하는 JavaScript 코드를 작성해볼까요? memo.js 파일을 만들고 아래 코드를 작성합니다. 각 함수와 로직에 상세한 주석을 달았으니 차근차근 이해해보세요.
// DOM 요소 참조const memoInput = document.getElementById('memoInput');const saveBtn = document.getElementById('saveBtn');const newBtn = document.getElementById('newBtn');const clearBtn = document.getElementById('clearBtn');const memoList = document.getElementById('memoList');// localStorage에서 사용할 키 이름const STORAGE_KEY = 'bear_memo_app_memos';// 페이지 로드 시 저장된 메모 목록을 렌더링document.addEventListener('DOMContentLoaded', loadMemos);// 1. 메모 저장 함수saveBtn.addEventListener('click', saveMemo);function saveMemo() {const memoText = memoInput.value.trim();if (memoText === '') {alert('메모 내용을 입력해주세요!');return;}// 기존 메모 배열을 가져오거나, 없으면 새 배열 생성const memos = getMemosFromStorage();// 새 메모 객체 생성 (고유 ID와 시간戳 추가)const newMemo = {id: new Date().getTime(), // 간단한 고유 ID 생성content: memoText,createdAt: new Date().toLocaleString('ko-KR') // 한국 시간 형식};// 새 메모를 배열에 추가memos.push(newMemo);// 변경된 배열을 localStorage에 저장 (문자열로 변환)localStorage.setItem(STORAGE_KEY, JSON.stringify(memos));// 입력창 초기화 및 목록 새로고침memoInput.value = '';loadMemos();alert('메모가 저장되었습니다!');}// 2. localStorage에서 메모 배열을 가져오는 헬퍼 함수function getMemosFromStorage() {const storedMemos = localStorage.getItem(STORAGE_KEY);// 저장된 데이터가 있으면 파싱, 없으면 빈 배열 반환return storedMemos ? JSON.parse(storedMemos) : [];}// 3. 메모 목록을 화면에 렌더링하는 함수function loadMemos() {const memos = getMemosFromStorage();memoList.innerHTML = ''; // 기존 목록 초기화if (memos.length === 0) {memoList.innerHTML = '<li class="memo-item" style="justify-content:center; color:#95a5a6;">저장된 메모가 없습니다.</li>';return;}// 메모 배열을 역순으로(최신순) 표시memos.reverse().forEach(memo => {const li = document.createElement('li');li.className = 'memo-item';li.dataset.id = memo.id; // 데이터 속성에 ID 저장li.innerHTML = `<div class="memo-content"><div>${memo.content}</div><div class="memo-date">작성일: ${memo.createdAt}</div></div><button class="delete-btn" onclick="deleteMemo(event, ${memo.id})">삭제</button>`;// 메모 아이템 클릭 시 편집창에 불러오기li.addEventListener('click', (e) => {// 삭제 버튼을 클릭한 경우는 제외if (e.target.classList.contains('delete-btn')) return;memoInput.value = memo.content;// 현재 불러온 메모의 ID를 저장 (수정 시 활용 가능)memoInput.dataset.currentId = memo.id;});memoList.appendChild(li);});}// 4. 개별 메모 삭제 함수 (이벤트 버블링 방지를 위해 event 객체 전달)function deleteMemo(event, id) {event.stopPropagation(); // 상위 li 요소의 클릭 이벤트 발생 방지if (!confirm('정말 이 메모를 삭제하시겠습니까?')) return;const memos = getMemosFromStorage();// 전달받은 id와 일치하지 않는 메모만 필터링const updatedMemos = memos.filter(memo => memo.id !== id);// 필터링된 새 배열을 저장localStorage.setItem(STORAGE_KEY, JSON.stringify(updatedMemos));loadMemos(); // 목록 다시 그리기alert('메모가 삭제되었습니다.');}// 5. 새 메모 작성 버튼newBtn.addEventListener('click', () => {memoInput.value = '';delete memoInput.dataset.currentId; // 현재 편집 중인 ID 정보 제거memoInput.focus();});// 6. 전체 삭제 버튼clearBtn.addEventListener('click', () => {if (getMemosFromStorage().length === 0) {alert('삭제할 메모가 없습니다.');return;}if (confirm('정말 모든 메모를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.')) {localStorage.removeItem(STORAGE_KEY); // 또는 clear()loadMemos();memoInput.value = '';alert('모든 메모가 삭제되었습니다.');}});// (선택) 입력창에서 Ctrl+Enter 눌러 저장하는 기능memoInput.addEventListener('keydown', (e) => {if (e.ctrlKey && e.key === 'Enter') {saveMemo();}});
이제 완성되었습니다! HTML 파일을 브라우저에서 열고 메모를 입력, 저장, 삭제해보세요. 크롬 개발자 도구(F12)의 ‘Application’ 탭에서 ‘Local Storage’ 항목을 보면 우리가 저장한 데이터가 실제로 키-값 쌍으로 들어가 있는 것을 확인할 수 있습니다. 이것이 바로 localStorage의 힘이죠.
정확한 시간 기록이 필요한 실험이나 트레이닝에는 실시간 스톱워치 기능을 활용하는 것이 좋습니다.
지금까지 localStorage의 기본 개념부터 실전 적용까지, 나만의 메모장 웹 앱을 만들어보는 시간을 가졌습니다. 이 작은 프로젝트 하나로 우리는 클라이언트 사이드 데이터 저장의 핵심 원리, JSON의 직렬화/역직렬화, DOM 이벤트 핸들링, 그리고 기본적인 CRUD(생성, 읽기, 수정, 삭제) 로직을 경험해볼 수 있었습니다.
이 메모장은 더 많은 기능으로 확장할 수 있습니다. 예를 들어, 메모 수정 기능을 추가하거나, 메모에 카테고리 태그를 달고 필터링하는 기능, 심지어는 다크 모드 지원과 같은 사용자 설정을 localStorage에 저장하는 것도 좋은 아이디어겠죠. 여러분의 상상력에 따 라 이 프로젝트를 더 풍성하게 키워나가 보세요.
프론트엔드 개발의 매력은 이렇게 직접 만든 것이 눈앞에서 동작한다는 것입니다. localStorage는 그런 경험을 시작하게 해주는 훌륭한 도구입니다. 다음 시간에는 localStorage의 한계를 넘어서는 IndexedDB나 서버와의 연동에 대해 다뤄보도록 하겠습니다. 오늘도 코딩하는곰과 함께 즐거운 코딩 시간이 되셨길 바랍니다. 질문이나 아이디어가 있으시면 댓글로 남겨주세요! 감사합니다. 🐻💻
QR코드로 간편하게 번호를 확인하고 싶다면, AI 번호 추천과 최근 당첨번호까지 제공하는 지니로또AI 앱을 다운로드하세요.
