이전까지 메모장에는 기본 CRUD 기능이 들어갔다.
Create → 메모 추가
Read → 메모 목록 보기
Update → 메모 수정
Delete → 메모 삭제
그리고 localStorage를 사용해서 새로고침 후에도 메모가 유지되도록 만들었다.
이번 단계에서는 새로운 기능을 크게 추가하기보다는, 기존 코드를 더 이해하기 좋은 구조로 정리했다.
핵심 목표는 다음과 같다.
- 추가, 수정, 삭제 로직을 함수로 분리하기
- 입력값 앞뒤 공백 처리하기
- 삭제 전 확인창 추가하기
왜 함수로 분리했나
처음에는 버튼 이벤트 안에 모든 로직이 들어 있었다.
예를 들어 삭제 버튼 이벤트 안에서 바로 배열을 수정하고, 저장하고, 다시 렌더링했다.
deleteButton.addEventListener('click', function () {
memos = memos.filter(function (item) {
return item.id !== memo.id;
});
saveMemos();
renderMemos();
});
이 방식도 동작은 한다.
하지만 코드가 길어질수록 이벤트 안에 너무 많은 일이 들어가게 된다.
이벤트 안에 추가, 수정, 삭제 로직이 그대로 들어 있으면 나중에 코드를 읽을 때 흐름을 파악하기 어려워질 수 있다.
그래서 역할별로 함수를 나눴다.
addMemo()
= 메모 추가 담당
editMemo()
= 메모 수정 담당
deleteMemo()
= 메모 삭제 담당
renderMemos()
= 화면 렌더링 담당
saveMemos()
= localStorage 저장 담당
loadMemos()
= localStorage 불러오기 담당
이렇게 나누면 각 함수가 어떤 일을 하는지 더 명확하게 보인다.
현재 코드 구조
현재 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 saveMemos() {
...
}
function loadMemos() {
...
}
function updateEmptyMessage() {
...
}
function deleteMemo(id) {
...
}
function editMemo(id) {
...
}
function addMemo(title, content) {
...
}
function renderMemos() {
...
}
addMemoButton.addEventListener('click', function () {
...
});
loadMemos();
renderMemos();
위에서부터 보면 흐름이 이전보다 조금 더 명확하다.
1. HTML 요소 가져오기
2. 메모 데이터 배열 준비
3. 저장/불러오기 함수
4. 빈 메시지 처리 함수
5. 삭제/수정/추가 함수
6. 렌더링 함수
7. 이벤트 등록
8. 초기 실행
삭제 로직 분리하기
먼저 삭제 로직을 deleteMemo(id) 함수로 분리했다.
function deleteMemo(id) {
const isConfirmed = confirm("정말 삭제하시겠습니까?");
if (!isConfirmed) {
return;
}
memos = memos.filter(function (item) {
return item.id !== id;
});
saveMemos();
renderMemos();
}
이 함수는 삭제할 메모의 id를 매개변수로 받는다.
삭제 버튼을 클릭했을 때는 해당 메모의 id를 넘겨준다.
deleteButton.addEventListener('click', function () {
deleteMemo(memo.id);
});
흐름은 다음과 같다.
삭제 버튼 클릭
→ deleteMemo(memo.id) 호출
→ confirm으로 삭제 여부 확인
→ 취소하면 return
→ 확인하면 memos 배열에서 해당 id 제거
→ saveMemos()
→ renderMemos()
confirm으로 삭제 확인하기
삭제는 실수로 누를 수 있기 때문에 확인창을 추가했다.
const isConfirmed = confirm("정말 삭제하시겠습니까?");
confirm()은 사용자가 누른 버튼에 따라 값을 반환한다.
확인 → true
취소 → false
그래서 취소를 누른 경우에는 바로 함수를 종료한다.
if (!isConfirmed) {
return;
}
이렇게 하면 사용자가 취소를 눌렀을 때 삭제 로직이 실행되지 않는다.
수정 로직 분리하기
수정 로직은 editMemo(id) 함수로 분리했다.
function editMemo(id) {
const targetMemo = memos.find(function (item) {
return item.id === id;
});
const newTitle = prompt("새 제목을 입력하세요", targetMemo.title.trim());
const newContent = prompt("새 내용을 입력하세요", targetMemo.content.trim());
if (newTitle === null || newContent === null) {
return;
}
if (newTitle.trim() === '' || newContent.trim() === '') {
alert("제목과 내용을 모두 입력하세요.");
return;
}
memos = memos.map(function (item) {
if (item.id === id) {
return {
id: item.id,
title: newTitle.trim(),
content: newContent.trim()
};
}
return item;
});
saveMemos();
renderMemos();
}
수정 버튼을 클릭하면 해당 메모의 id를 넘긴다.
editButton.addEventListener('click', function () {
editMemo(memo.id);
});
수정할 메모 찾기
editMemo(id) 함수는 id만 받는다.
그런데 기존 제목과 내용을 prompt에 보여주려면, 먼저 memos 배열에서 해당 id를 가진 메모를 찾아야 한다.
const targetMemo = memos.find(function (item) {
return item.id === id;
});
find()는 배열에서 조건에 맞는 첫 번째 요소를 찾는다.
여기서는 다음 조건에 맞는 메모를 찾는다.
item.id가 수정하려는 id와 같은 메모
찾은 메모를 targetMemo에 담고, 기존 제목과 내용을 prompt의 기본값으로 사용했다.
const newTitle = prompt("새 제목을 입력하세요", targetMemo.title.trim());
const newContent = prompt("새 내용을 입력하세요", targetMemo.content.trim());
수정 취소 처리
사용자가 prompt에서 취소를 누르면 결과는 null이다.
if (newTitle === null || newContent === null) {
return;
}
이렇게 처리해서 취소 시에는 수정 작업을 중단한다.
수정 시 공백 입력 방지
수정할 때 공백만 입력하는 것도 막았다.
if (newTitle.trim() === '' || newContent.trim() === '') {
alert("제목과 내용을 모두 입력하세요.");
return;
}
trim()은 문자열 앞뒤 공백을 제거한다.
예를 들어 다음 값은 공백만 있는 문자열이다.
" "
여기에 trim()을 적용하면 빈 문자열이 된다.
" ".trim()
결과는 다음과 같다.
""
그래서 공백만 입력한 경우도 빈 값으로 판단할 수 있다.
저장할 때도 앞뒤 공백이 제거된 값으로 저장했다.
title: newTitle.trim(),
content: newContent.trim()
map으로 해당 메모 수정하기
수정할 때는 map()을 사용했다.
memos = memos.map(function (item) {
if (item.id === id) {
return {
id: item.id,
title: newTitle.trim(),
content: newContent.trim()
};
}
return item;
});
이 코드는 memos 배열을 새 배열로 다시 만드는 코드다.
동작 방식은 다음과 같다.
1. memos 배열을 하나씩 확인한다.
2. 수정하려는 id와 같은 메모를 만나면 새 제목/내용을 가진 객체를 반환한다.
3. 수정 대상이 아니면 기존 item을 그대로 반환한다.
즉, 전체 배열을 돌지만 실제로 바뀌는 것은 id가 같은 메모 하나뿐이다.
추가 로직 분리하기
추가 로직도 addMemo(title, content) 함수로 분리했다.
function addMemo(title, content) {
if (title === '' || content === '') {
alert('제목과 내용을 모두 입력하세요.');
return;
}
const newMemo = {
id: Date.now(),
title: title,
content: content
};
memos.push(newMemo);
saveMemos();
renderMemos();
}
이 함수는 제목과 내용을 받아서 새 메모 객체를 만든다.
const newMemo = {
id: Date.now(),
title: title,
content: content
};
그리고 memos 배열에 추가한다.
memos.push(newMemo);
그 후 localStorage에 저장하고 화면을 다시 그린다.
saveMemos();
renderMemos();
추가 버튼 이벤트 정리
추가 버튼 이벤트는 이전보다 짧아졌다.
addMemoButton.addEventListener('click', function () {
const title = titleInput.value.trim();
const content = contentInput.value.trim();
addMemo(title, content);
titleInput.value = '';
contentInput.value = '';
});
입력값을 가져올 때 trim()을 사용했다.
const title = titleInput.value.trim();
const content = contentInput.value.trim();
그래서 처음부터 앞뒤 공백을 제거한 값을 addMemo()에 넘긴다.
trim을 추가와 수정 둘 다 적용한 이유
처음에는 추가할 때만 trim()을 하면 충분할 것 같았다.
하지만 추가와 수정은 서로 다른 시점에 입력값을 받는다.
추가할 때 입력값
→ titleInput.value, contentInput.value
수정할 때 입력값
→ prompt에서 받은 newTitle, newContent
따라서 추가할 때 한 번 trim()했다고 해서, 수정할 때 입력한 값까지 자동으로 정리되는 것은 아니다.
그래서 수정할 때도 별도로 trim()을 적용했다.
newTitle.trim()
newContent.trim()
정리하면 다음과 같다.
추가 입력값은 추가 시점에 trim
수정 입력값은 수정 시점에 trim
renderMemos의 역할 유지
renderMemos()는 여전히 화면을 다시 그리는 역할이다.
function renderMemos() {
memoContainer.innerHTML = '';
memos.forEach(function (memo) {
...
});
updateEmptyMessage();
}
이 함수는 데이터를 직접 추가하거나 수정하거나 삭제하지 않는다.
현재 memos 배열을 기준으로 화면을 다시 만든다.
다만 메모 카드를 만들면서 각 카드의 수정/삭제 버튼에 이벤트를 연결한다.
editButton.addEventListener('click', function () {
editMemo(memo.id);
});
deleteButton.addEventListener('click', function () {
deleteMemo(memo.id);
});
이제 이벤트 안에는 복잡한 로직이 직접 들어가지 않고, 각각의 함수 호출만 남았다.
수정 버튼 클릭 → editMemo(memo.id)
삭제 버튼 클릭 → deleteMemo(memo.id)
이번 단계에서 정리된 구조
이번 단계 후 전체 구조는 이전보다 명확해졌다.
addMemo()
→ 메모 추가 담당
editMemo()
→ 메모 수정 담당
deleteMemo()
→ 메모 삭제 담당
saveMemos()
→ localStorage 저장 담당
loadMemos()
→ localStorage 불러오기 담당
renderMemos()
→ 화면 렌더링 담당
updateEmptyMessage()
→ 빈 목록 메시지 처리 담당
각 함수가 맡은 역할이 분리되면서, 코드를 읽을 때 흐름을 따라가기 쉬워졌다.
현재 완성 상태
현재 메모장은 다음 기능을 가진다.
메모 추가
메모 목록 조회
메모 수정
메모 삭제
삭제 전 확인
공백 입력 방지
줄바꿈 유지
localStorage 저장
새로고침 후 데이터 유지
함수 단위 코드 정리
'Project > Memo Evolution' 카테고리의 다른 글
| #11 React 시작하기 (0) | 2026.06.15 |
|---|---|
| #10 React로 넘어가기 전에 컴포넌트 이해하기 (0) | 2026.06.15 |
| #8 메모 수정 기능 구현 (0) | 2026.06.13 |
| #7 localStorage로 메모 저장하기 (0) | 2026.06.13 |
| #6 메모 내용 줄바꿈 처리하기 (0) | 2026.06.11 |