메모 데이터를 배열로 관리하기
이전까지는 메모를 추가하면 바로 HTML 요소를 만들고 화면에 붙였다.
흐름은 단순했다.
입력값 → HTML 요소 생성 → 화면에 바로 추가
하지만 이 방식은 메모 데이터가 따로 관리되지 않는다는 문제가 있었다.
화면에 메모가 보이기는 하지만, JavaScript 입장에서는 “현재 어떤 메모들이 있는지”를 명확하게 관리하기 어렵다.
그래서 이번 단계에서는 메모 데이터를 배열로 관리하도록 구조를 바꿨다.
입력값 → 메모 객체 생성 → memos 배열에 저장 → 배열을 기준으로 화면 다시 그리기
즉, 이제 메모의 원본은 화면이 아니라 memos 배열이다.
왜 배열로 관리해야 하나
처음에는 메모를 추가할 때 바로 화면에 붙이는 방식이 더 단순해 보였다.
하지만 삭제, 수정, 저장 기능으로 넘어가려면 문제가 생긴다.
예를 들어 다음과 같은 질문이 생긴다.
삭제할 때 어떤 메모를 지울 것인가?
수정할 때 어떤 메모를 수정할 것인가?
localStorage에는 무엇을 저장할 것인가?
나중에 서버나 DB에는 어떤 데이터를 보낼 것인가?
HTML은 화면에 보여주기 위한 결과물이다.
데이터 자체를 관리하기에는 적절하지 않다.
그래서 메모를 먼저 JavaScript 데이터로 관리하기로 했다.
let memos = [];
이 배열 안에 메모 객체들이 들어간다.
예를 들어 메모가 2개라면 데이터는 이런 형태가 된다.
[
{
id: 1,
title: '첫 번째 메모',
content: '내용 1'
},
{
id: 2,
title: '두 번째 메모',
content: '내용 2'
}
]
이제 화면은 이 배열을 기준으로 그려진다.
최종 코드
이번 단계에서 작성한 script.js는 다음과 같다.
const titleInput = document.getElementById("memo-title");
const contentInput = document.getElementById("memo-content");
const addMemoButton = document.getElementById("add-memo-button");
const memoContainer = document.getElementById("memo-container");
const emptyMessage = document.getElementById("empty-message");
let memos = [];
function updateEmptyMessage() {
if (memos.length === 0) {
emptyMessage.style.display = 'block';
} else {
emptyMessage.style.display = 'none';
}
}
function renderMemos() {
memoContainer.innerHTML = '';
memos.forEach(function (memo) {
const memoCard = document.createElement('div');
memoCard.className = 'memo-card';
memoCard.innerHTML = `
<h3>${memo.title}</h3>
<p>${memo.content}</p>
<button type="button">삭제</button>
`;
const deleteButton = memoCard.querySelector('button');
deleteButton.addEventListener('click', function () {
memos = memos.filter(function (item) {
return item.id !== memo.id;
});
renderMemos();
});
memoContainer.appendChild(memoCard);
});
updateEmptyMessage();
}
addMemoButton.addEventListener('click', function () {
const title = titleInput.value.trim();
const content = contentInput.value.trim();
if (title === '' || content === '') {
alert('제목과 내용을 모두 입력하세요.');
return;
}
const newMemo = {
id: Date.now(),
title: title,
content: content
};
memos.push(newMemo);
renderMemos();
titleInput.value = '';
contentInput.value = '';
});
renderMemos 함수
이번 단계에서 가장 중요한 함수는 renderMemos()다.
function renderMemos() {
memoContainer.innerHTML = '';
memos.forEach(function (memo) {
...
});
updateEmptyMessage();
}
처음에는 renderMemos()를 단순히 화면에 메모를 보여주는 함수라고 생각했다.
하지만 더 정확히는 다음과 같다.
renderMemos()
= 현재 memos 배열 상태와 화면을 똑같이 맞추는 함수
배열에 메모가 2개 있으면 화면에도 2개가 보여야 한다.
배열이 비어 있으면 화면에서도 메모가 보이면 안 된다.
즉, 화면은 memos 배열을 보고 다시 만들어지는 결과물이다.
왜 화면을 먼저 비우는가
renderMemos() 안에는 다음 코드가 있다.
memoContainer.innerHTML = '';
처음에는 이 코드가 메모를 전부 삭제하는 것처럼 보여서 헷갈렸다.
하지만 이 코드는 데이터를 삭제하는 코드가 아니다.
memos 배열은 그대로 두고, 화면에 이미 그려져 있던 메모 카드만 지운다.
이유는 renderMemos()가 기존 화면에 계속 추가하는 함수가 아니라, 현재 배열 상태를 기준으로 화면을 다시 만드는 함수이기 때문이다.
예를 들어 배열에 A, B가 있다고 하자.
memos = [A, B];
이미 화면에 A가 그려져 있는 상태에서 화면을 비우지 않고 A, B를 다시 붙이면 화면은 이렇게 될 수 있다.
A A B
A가 중복된다.
그래서 먼저 화면을 비운다.
memoContainer.innerHTML = '';
그다음 현재 memos 배열을 기준으로 다시 그린다.
A B
삭제할 때도 마찬가지다.
화면에는 A, B, C가 있고, 배열에서 B를 삭제해 memos = [A, C]가 되었다고 하자.
화면을 비우지 않고 다시 그리면 기존 화면 위에 A, C가 추가될 수 있다.
A B C A C
그래서 renderMemos()는 항상 다음 순서로 동작한다.
1. 기존 화면을 비운다.
2. 현재 memos 배열에 있는 메모만 다시 그린다.
forEach로 배열을 하나씩 그리기
화면을 비운 뒤에는 memos 배열을 순회한다.
memos.forEach(function (memo) {
...
});
forEach는 배열 안의 요소를 하나씩 꺼내서 처리하는 메서드다.
예를 들어 배열이 이렇게 있다면,
memos = [
{ id: 1, title: 'A', content: '내용 A' },
{ id: 2, title: 'B', content: '내용 B' }
];
첫 번째 반복에서는 memo에 첫 번째 객체가 들어간다.
{ id: 1, title: 'A', content: '내용 A' }
두 번째 반복에서는 memo에 두 번째 객체가 들어간다.
{ id: 2, title: 'B', content: '내용 B' }
즉, memo는 배열 전체가 아니라 현재 처리 중인 메모 하나다.
각 메모마다 새 메모 카드를 만들고, 화면에 붙인다.
const memoCard = document.createElement('div');
memoCard.className = 'memo-card';
memoCard.innerHTML = `
<h3>${memo.title}</h3>
<p>${memo.content}</p>
<button type="button">삭제</button>
`;
memoContainer.appendChild(memoCard);
삭제 기능 변경
이전에는 삭제 버튼을 누르면 화면의 메모 카드를 직접 제거하는 방식으로 생각했다.
memoCard.remove();
하지만 이제 메모의 원본은 화면이 아니라 memos 배열이다.
그래서 삭제도 화면에서 먼저 하는 것이 아니라, 배열에서 먼저 해야 한다.
deleteButton.addEventListener('click', function () {
memos = memos.filter(function (item) {
return item.id !== memo.id;
});
renderMemos();
});
이 코드는 현재 삭제하려는 메모와 id가 다른 메모만 남기는 코드다.
즉, 삭제할 메모를 제외한 새 배열을 만들고, 그 배열로 memos를 다시 교체한다.
예를 들어 현재 배열이 다음과 같다고 하자.
memos = [
{ id: 1, title: 'A' },
{ id: 2, title: 'B' },
{ id: 3, title: 'C' }
];
B의 삭제 버튼을 눌렀다면 삭제하려는 id는 2다.
return item.id !== 2;
각 메모를 검사하면 다음과 같다.
id 1 !== 2 → true → 남김
id 2 !== 2 → false → 제외
id 3 !== 2 → true → 남김
결과적으로 새 배열은 이렇게 된다.
[
{ id: 1, title: 'A' },
{ id: 3, title: 'C' }
]
그리고 이 새 배열을 다시 memos에 저장한다.
memos = memos.filter(...);
배열을 바꾼 뒤에는 화면도 다시 맞춰야 한다.
renderMemos();
renderMemos()가 실행되면 기존 화면을 비우고, 현재 memos 배열에 남아 있는 메모만 다시 그린다.
그래서 화면에서도 삭제된 메모가 사라진다.
빈 메시지도 배열 기준으로 관리하기
이전에는 화면에 있는 메모 카드 개수를 기준으로 빈 메시지를 관리할 수도 있었다.
하지만 이제 메모의 원본은 memos 배열이다.
그래서 빈 메시지도 배열 기준으로 판단하는 것이 더 자연스럽다.
function updateEmptyMessage() {
if (memos.length === 0) {
emptyMessage.style.display = 'block';
} else {
emptyMessage.style.display = 'none';
}
}
memos.length가 0이면 메모가 없는 상태다.
그래서 “아직 작성된 메모가 없습니다.” 문구를 보여준다.
반대로 memos.length가 1 이상이면 메모가 있는 상태이므로 빈 메시지를 숨긴다.
메모 추가 흐름
메모 추가 버튼을 누르면 다음 흐름으로 동작한다.
addMemoButton.addEventListener('click', function () {
const title = titleInput.value.trim();
const content = contentInput.value.trim();
if (title === '' || content === '') {
alert('제목과 내용을 모두 입력하세요.');
return;
}
const newMemo = {
id: Date.now(),
title: title,
content: content
};
memos.push(newMemo);
renderMemos();
titleInput.value = '';
contentInput.value = '';
});
정리하면 다음과 같다.
1. 제목과 내용 입력값을 가져온다.
2. 앞뒤 공백을 제거한다.
3. 빈 값이면 alert를 띄우고 중단한다.
4. 새 메모 객체를 만든다.
5. memos 배열에 새 메모를 추가한다.
6. renderMemos()로 화면을 다시 그린다.
7. 입력창을 비운다.
여기서 Date.now()는 메모마다 고유한 id를 만들기 위해 사용했다.
id: Date.now()
현재 시간을 숫자로 반환하기 때문에 간단한 고유값으로 사용할 수 있다.
나중에 서버와 DB를 사용하면 이 id는 데이터베이스에서 생성하는 값으로 바뀔 수 있다.
이번 단계에서 가장 중요한 변화는 메모의 기준이 바뀐 것이다.
이전: 화면에 추가된 HTML이 기준
현재: memos 배열이 기준
이제 화면은 데이터를 직접 관리하는 곳이 아니라, 배열을 보여주는 결과물에 가깝다.
이번 단계에서 정리한 내용은 다음과 같다.
- 메모를 객체로 만들기
- 메모 객체를 배열에 저장하기
- 배열을 기준으로 화면 다시 그리기
- renderMemos() 함수로 화면 렌더링 분리하기
- forEach()로 배열 순회하기
- filter()로 특정 메모 삭제하기
- 삭제 후 다시 렌더링하기
- 빈 메시지도 배열 기준으로 관리하기
- Date.now()로 임시 id 만들기
현재 단계의 한계
현재는 메모를 배열로 관리하지만, 아직 브라우저 메모리에만 존재한다.
그래서 새로고침하면 memos 배열이 다시 빈 배열로 초기화된다.
let memos = [];
즉, 화면에 추가한 메모는 새로고침 후 사라진다.
아직 부족한 점은 다음과 같다.
- 새로고침하면 메모가 사라진다.
- localStorage 저장 기능이 없다.
- 수정 기능이 없다.
- 삭제할 때 확인 메시지가 없다.
- 메모 데이터가 서버나 DB에 저장되지 않는다.
다음 단계에서는 localStorage를 사용해서 메모 데이터를 브라우저에 저장할 예정이다.
현재는 메모를 배열에만 저장하고 있기 때문에 새로고침하면 사라진다.
다음에는 memos 배열을 localStorage에 저장하고, 페이지가 다시 열릴 때 저장된 메모를 불러오는 방식으로 바꿔볼 예정이다.
이번 단계에서 데이터의 기준을 배열로 옮겼기 때문에, 다음 단계에서 localStorage 저장으로 넘어가기가 훨씬 쉬워졌다.
'Project > Memo Evolution' 카테고리의 다른 글
| #7 localStorage로 메모 저장하기 (0) | 2026.06.13 |
|---|---|
| #6 메모 내용 줄바꿈 처리하기 (0) | 2026.06.11 |
| #4 입력한 메모를 화면에 추가하기 (0) | 2026.06.10 |
| #3 JavaScript 연결하고 버튼 클릭 감지 (0) | 2026.06.10 |
| #2 CSS로 메모장 화면 꾸미기 (0) | 2026.06.10 |