광고 없는 나만의 To Do List 개발 일지 - 3. todo 추가/수정/삭제 기능 구현하기

Hanbin·2026년 2월 10일

나만의 To Do List

목록 보기
3/3
post-thumbnail

이번 글에서는 To Do List의 핵심 기능인 추가 / 삭제 / 수정 기능을 구현하면서 겪었던 고민과 과정들을 다뤄 보려고 합니다.

이전 글(2. 화면에 todo 렌더링하기)에 이어진 글입니다 :)




1. todo 추가 기능

todo 추가 기능은 비교적 구현 난이도가 쉬운 편이었다.
전체 흐름은 입력값 검증 → todos 배열 업데이트 → 다시 렌더링 구조로 잡았다.

function addTodo() {
  if (document.getElementById("input-value").value.trim() == "") {
    // 1. 입력 input 값이 빈 값이라면 해당 input에 focus
    document.getElementById("input-value").focus();
  } else {
    // 2. 작성한 내용의 객체를 todos 배열에 추가
    todos.push({
      id: todos.length,
      contents: document.getElementById("input-value").value,
      isDone: false,
    });

    // 3. todos 배열 렌더링
    renderTodos(todos);

    // 4. 추가 완료됐으면 input 값 초기화
    document.getElementById("input-value").value = "";
  }
}

todo 추가 기능은 아래와 같은 알고리즘으로 설계했다.

  1. 입력하려는 todo 내용이 빈 값인지 체크
  2. 작성한 내용을 하나의 todo 객체로 만들어 todos 배열에 추가
  3. 변경된 todos 배열을 다시 렌더링
  4. 추가가 완료되면 input의 value를 빈 값으로 초기화



2. todo 삭제 기능

삭제 기능은 처음 생각보다 고민이 많았다.

1) 먼저 정한 기준

  • 삭제 버튼을 누르면 바로 삭제되도록 했다
  • 나중에는 수정/삭제를 선택할 수 있는 모달을 띄울 계획이었다
  • 삭제된 todo를 따로 관리할 필요는 없다고 판단했다

그래서 아래 두 가지 방법을 두고 고민했다.

  • todo 객체에 isDelete 같은 프로퍼티를 추가해서 관리할지
  • todos 배열에서 해당 todo를 아예 제거할지

결론은 후자였다.
현재 서비스에서는 삭제된 todo를 다시 활용할 계획이 없었기 때문이다.


첫 번째 구현과 문제 발생

삭제 로직은 아래와 같이 설계했다.

  1. 클릭한 todo의 id 값을 가져왔다
  2. todos 배열에서 해당 id를 가진 요소를 찾아 splice(id, 1)을 호출했다
  3. 삭제 후 renderTodos(todos)로 다시 렌더링했다

하지만 예상치 못한 문제가 발생했다.

  • 중간에 있는 todo는 정상적으로 삭제됐다
  • 마지막 todo 하나가 남았을 때 더 이상 삭제되지 않는 이슈가 발생했다

특이하게도, 순서대로 삭제하면 또 잘 동작했다.


원인 분석

원인은 splice 메서드의 특성 때문이었다.

splice는 배열의 인덱스를 기준으로 동작하는데,
todo를 삭제하는 순간부터 id !== index 상태가 되어버렸다

즉,

  • 처음에는 id === index라서 문제가 없었고
  • 하나라도 삭제되면 배열 인덱스가 재정렬되면서
  • 기존 id 기준 삭제 로직이 깨지고 있었다

그럼 todo 객체에 바로 접근할 수 없을까?

이런 고민도 들었다.

“삭제 버튼 클릭 시, 해당 todo 객체로 바로 접근할 수는 없을까?”

하지만 구조적으로 어렵다는 걸 알게 됐다.

  • UI(HTML)는 JavaScript 객체와 완전히 분리되어 있었고
  • 결국 data-id 같은 식별자를 통해서만 연결할 수 있었다

이 과정에서
UI는 상태를 보여주는 결과물이고, 실제 데이터는 JS가 관리한다
라는 개념이 더 명확해졌다.




3. todo 수정 기능

이번 구현에서 가장 많이 막혔던 부분이 수정 기능이었다.

UX 먼저 고민

  • 수정 버튼을 누르면 todo 내용에 커서가 생기면 좋겠다고 생각했다
  • 기존에는 span 태그 안에 todo 내용을 넣고 있었다

여기서 이런 고민이 들었다.

“클릭해서 수정하려면, 애초에 input 박스로 렌더링하는 게 맞지 않을까?”

그래서 todo 내용을 input 박스로 렌더링하는 방향으로 변경했다.


수정 버튼 클릭 시 동작

의도한 흐름은 다음과 같았다.

  • 수정 버튼 클릭
  • 해당 todo의 input을 disabled → false로 변경
  • 포커스를 input으로 이동
  • 수정 중임을 알 수 있도록 버튼 색상 변경

이를 위해 editTodo() 함수를 작성했다.

function editTodo(event) {
  const targetEditButton = event.target;
  targetEditButton.disabled = true;

  const targetTodo =
    event.target.closest('div').parentNode
      .getElementsByClassName('todo-inputbox')[0];

  targetTodo.disabled = false;
  targetTodo.focus();
}

CSS는 아래와 같이 작성했다.

.edit-text { 
  font-size: 15px;
  color: #aaa;
  line-height: 1;
  background-color: #eee;
  padding: 6px;
  border-radius: 3px;
}

.edit-text:disabled {  
  color: whitesmoke;
  background-color: #ddd;
}

.edit-text:hover {
  transition: 0.2s;
  color: whitesmoke;
  background-color: #ddd;
}

하지만 아무리 해도 disabled 스타일이 적용되지 않는 문제가 발생했다.


원인: span 태그는 disabled를 지원하지 않는다

  • 수정 버튼을 span 태그로 구현했는데
  • disabled 속성은 모든 태그에서 지원되지 않았다

MDN 문서를 통해 확인했다.
출처 : https://developer.mozilla.org/ko/docs/Web/HTML/Reference/Attributes/disabled

해결 방법
span 태그를 button 태그로 변경했다

<button type="button" class="edit-button" onclick="editTodo(event)">
  수정
</button>

또 다른 문제: disabled가 되면 다시 클릭할 수 없다

다음으로 마주한 문제는 이것이었다.

function editTodo(event) {
  if (event.target.disabled == true) {
    event.target.disabled = false;
  } else {
    event.target.disabled = true;
  }
}

이렇게 토글하려 했지만,
disabled가 되는 순간 버튼이 아예 클릭되지 않았다.

즉,

  • 수정 중 → disabled
  • disabled 상태에서는 다시 클릭할 수 없음
  • 결과적으로 수정한 내용을 저장할 방법이 사라졌다

해결 방향

문제를 정리해보니 핵심은 이것이었다.

  • 수정 상태를 disabled 하나로 표현하려 했던 점

그래서 방향을 다음과 같이 바꿨다.

  • 수정 중일 때: editing-button
  • 수정 중이 아닐 때: edit-button
  • 상태는 JavaScript 로직으로 관리
  • 스타일은 클래스 네임으로 분리

이 방식이 훨씬 명확했고,
기능 / 상태 / 스타일이 조금씩 분리되기 시작했다.


마무리

이번 구현을 통해 느낀 점은 다음과 같았다.

  • DOM 조작은 단순한 이벤트 처리 문제가 아니었다
  • 상태 관리와 굉장히 밀접하게 연결되어 있었다
  • “일단 되게 만들자”라는 접근은 결국 다시 돌아오게 만든다

다음 글에서는

  • todo 완료 처리
  • 로컬 스토리지 연동
  • 전체 구조 리팩토링

을 다룰 예정이다.
현재 코드는 동작은 하지만, 아직 구조적으로 예쁘지는 않은 상태다.
조금씩 다듬어가며 발전시켜볼 생각이다.

profile
Software Engineer

0개의 댓글