본문 바로가기
Javascript 프로젝트/02_To Do List

[To Do List] 4단계: 확장 가능한 프로젝트 구조

by cogito21_js 2024. 6. 16.
반응형

4. 확장 가능한 프로젝트 구조

프로젝트 구조를 확장 가능하게 설계하면 나중에 기능을 추가하거나 유지보수하기가 쉬워집니다. 다음은 Simple To-Do List 프로젝트의 확장 가능한 디렉토리 및 파일 구조 예시입니다:

simple-to-do-list/
│
├── index.html
├── css/
│   └── style.css
├── js/
│   ├── app.js
│   ├── storage.js
│   └── ui.js
└── assets/
    └── images/
        └── logo.png

디렉토리 및 파일 설명

  1. index.html:

    • 프로젝트의 메인 HTML 파일입니다. 전체 구조를 정의하고, 필요한 CSS와 JavaScript 파일을 포함합니다.
  2. css/style.css:

    • 모든 스타일 관련 정의가 들어있는 CSS 파일입니다. 필요에 따라 더 많은 CSS 파일을 생성할 수 있습니다.
  3. js/app.js:

    • 애플리케이션의 주요 로직이 들어있는 JavaScript 파일입니다. 다른 모듈을 통합하고 전체 애플리케이션을 관리합니다.
  4. js/storage.js:

    • 로컬 스토리지와의 상호작용을 처리하는 모듈입니다. 할 일 데이터를 저장하고 불러오는 기능을 포함합니다.
  5. js/ui.js:

    • 사용자 인터페이스 관련 기능을 처리하는 모듈입니다. 할 일 추가, 수정, 삭제 시 UI 업데이트를 담당합니다.
  6. assets/images/:

    • 프로젝트에서 사용하는 모든 이미지 파일을 저장하는 디렉토리입니다. 예시로 로고 이미지가 포함되어 있습니다.

각 파일의 예시 코드

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Simple To-Do List</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <div class="container">
    <h1>To-Do List</h1>
    <div class="input-container">
      <input type="text" id="todo-input" placeholder="Add a new task...">
      <button id="add-btn">Add</button>
    </div>
    <ul id="todo-list"></ul>
  </div>
  <script src="js/storage.js"></script>
  <script src="js/ui.js"></script>
  <script src="js/app.js"></script>
</body>
</html>

css/style.css

body {
  font-family: Arial, sans-serif;
  background-color: #f4f4f4;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  margin: 0;
}

.container {
  background: white;
  padding: 20px;
  border-radius: 5px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  width: 300px;
}

h1 {
  text-align: center;
}

.input-container {
  display: flex;
  justify-content: space-between;
  margin-bottom: 20px;
}

#todo-input {
  flex: 1;
  padding: 10px;
}

#add-btn {
  padding: 10px;
  margin-left: 10px;
}

#todo-list {
  list-style: none;
  padding: 0;
}

.todo-item {
  padding: 10px;
  background: #eee;
  margin-bottom: 10px;
  border-radius: 5px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.todo-item.completed {
  text-decoration: line-through;
  opacity: 0.6;
}

js/app.js

document.addEventListener('DOMContentLoaded', () => {
  const todoInput = document.getElementById('todo-input');
  const addBtn = document.getElementById('add-btn');
  const todoList = document.getElementById('todo-list');

  addBtn.addEventListener('click', () => {
    const todoText = todoInput.value.trim();
    if (todoText !== '') {
      addTodoItem(todoText);
      todoInput.value = '';
    }
  });

  todoList.addEventListener('click', (event) => {
    if (event.target.classList.contains('delete-btn')) {
      deleteTodoItem(event.target.parentElement);
    } else if (event.target.classList.contains('todo-item')) {
      toggleCompleteTodoItem(event.target);
    }
  });

  loadTodoItems();
});

function addTodoItem(todoText) {
  const todoItem = createTodoItemElement(todoText);
  saveTodoItem(todoText);
  document.getElementById('todo-list').appendChild(todoItem);
}

function deleteTodoItem(todoItem) {
  removeTodoItem(todoItem.textContent.trim());
  todoItem.remove();
}

function toggleCompleteTodoItem(todoItem) {
  todoItem.classList.toggle('completed');
  updateTodoItemStatus(todoItem.textContent.trim());
}

js/storage.js

function loadTodoItems() {
  const todoItems = JSON.parse(localStorage.getItem('todoItems')) || [];
  todoItems.forEach(item => {
    const todoItem = createTodoItemElement(item.text, item.completed);
    document.getElementById('todo-list').appendChild(todoItem);
  });
}

function saveTodoItem(todoText) {
  const todoItems = JSON.parse(localStorage.getItem('todoItems')) || [];
  todoItems.push({ text: todoText, completed: false });
  localStorage.setItem('todoItems', JSON.stringify(todoItems));
}

function removeTodoItem(todoText) {
  let todoItems = JSON.parse(localStorage.getItem('todoItems')) || [];
  todoItems = todoItems.filter(item => item.text !== todoText);
  localStorage.setItem('todoItems', JSON.stringify(todoItems));
}

function updateTodoItemStatus(todoText) {
  const todoItems = JSON.parse(localStorage.getItem('todoItems')) || [];
  const todoItem = todoItems.find(item => item.text === todoText);
  if (todoItem) {
    todoItem.completed = !todoItem.completed;
    localStorage.setItem('todoItems', JSON.stringify(todoItems));
  }
}

js/ui.js

function createTodoItemElement(todoText, completed = false) {
  const todoItem = document.createElement('li');
  todoItem.textContent = todoText;
  todoItem.classList.add('todo-item');
  if (completed) {
    todoItem.classList.add('completed');
  }

  const deleteBtn = document.createElement('button');
  deleteBtn.textContent = 'Delete';
  deleteBtn.classList.add('delete-btn');
  todoItem.appendChild(deleteBtn);

  return todoItem;
}
반응형