Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>투두 앱</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1 id="title"">To do list</h1>

<!-- 날짜별 조회 및 추가 날짜도 같이 사용 -->
<section id="get-by-date">
<label for="filterDate">날짜: </label>
<input type="date" id="filterDate" />
<button id="resetBtn">전체보기</button>
</section>

<!-- 투두 입력 영역 -->
<section class="inputDiv">
<input type="text" id="todoInput" placeholder="할 일을 입력하세요." />
<button id="addBtn">추가</button>
<span id="countDisplay"></span>
</section>

<!-- 투두 리스트 영역임 -->
<ul id="todoList"></ul>

<script src="script.js"></script>
</body>
</html>
109 changes: 109 additions & 0 deletions script.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전체보기를 통해서 모든 할 일들을 조회할 수 있는 점은 좋은 듯합니다. 하지만, 전체보기 후에 오늘 날짜를 보려면, 날짜 캘린더 들어가서 클릭 후, 날짜를 클릭해야한다는 점에서 사용자 ux측면에서 불편한듯합니다ㅠ

Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 오늘 날짜 YYYY-MM-DD 반환
function getToday() {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, "0");
const day = String(today.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}

document.addEventListener("DOMContentLoaded", () => {
const todoInput = document.getElementById("todoInput");
const addBtn = document.getElementById("addBtn");
const todoList = document.getElementById("todoList");
const filterDateInput = document.getElementById("filterDate"); // 날짜 입력 (추가+조회 겸용)
const filterBtn = document.getElementById("filterBtn");
const resetBtn = document.getElementById("resetBtn");
const countDisplay = document.getElementById("countDisplay");

// localStorage에서 불러오기
let todos = JSON.parse(localStorage.getItem("todos")) || [];

// 날짜 입력 기본값: 오늘
filterDateInput.value = getToday();

// 초기 렌더링
renderTodos(todos);

// 추가
addBtn.addEventListener("click", () => {
const text = todoInput.value.trim();
const date = filterDateInput.value;

if (text === "" || date === "") {
alert("할 일과 날짜를 입력하세요!");
return;
}

const todo = { id: Date.now(), text, date };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 todo id를 Date.now으로 저장하셨는데, 같은 밀리초안에 여러 투두를 등록하게 되는 경우, id가 꼬일 수 있습니다(동시성 문제).

date.now대신 uuid를 사용해보는 것을 어떠할까요?

http://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID

todos.push(todo);
saveTodos();
filterByDate(date);

todoInput.value = "";
filterDateInput.value = date; // 날짜 유지
});

// 조회
filterDateInput.addEventListener("change", () => {
filterByDate(filterDateInput.value);
});

function filterByDate(date) {
if (!date) return;

const filtered = todos.filter((t) => t.date === date);
renderTodos(filtered);
countDisplay.textContent = `${filtered.length}개`;
}

const title = document.getElementById("title"); // h1 선택
title.addEventListener("click", () => {
filterDateInput.value = getToday(); // 날짜 오늘로 초기화
filterByDate(filterDateInput.value); // 조회 버튼 없이 바로 조회
});
// 전체보기
resetBtn.addEventListener("click", () => {
renderTodos(todos);
countDisplay.textContent = "";
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이게 전체보기 버튼인데, 클릭하면 할일 개수가 사라지던데 그 기능이 맞는걸까요??
image

countDisplay.textContent = ""; 가 할일 개수를 빈 문자열로 바꾸는 거 같은데 어떤 기능인지 잘 모르겠습니다..
특히 다른 할일을 추가하면filterByDate(date);로 다시 개수는 업데이트 되어서 이 부분이 어떤 기능을 하는지 파악이 어려운 것 같습니다 ㅜㅜ


// 렌더링 함수
function renderTodos(list) {
todoList.innerHTML = "";

if (!list || list.length === 0) {
const empty = document.createElement("li");
empty.textContent = "표시할 할 일이 없습니다.";
empty.style.listStyle = "none";
todoList.appendChild(empty);
return;
}

list.forEach((todo) => {
const li = document.createElement("li");
li.dataset.id = todo.id;

const span = document.createElement("span");
span.textContent = `${todo.text} (${todo.date})`;

const delBtn = document.createElement("button");
delBtn.textContent = "삭제";
delBtn.classList.add("deleteBtn");

delBtn.addEventListener("click", () => {
todos = todos.filter((t) => t.id !== todo.id);
saveTodos();
renderTodos(todos);
});

li.appendChild(span);
li.appendChild(delBtn);
todoList.appendChild(li);
});
}

function saveTodos() {
localStorage.setItem("todos", JSON.stringify(todos));
}
});
87 changes: 87 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
body {
font-family: "Pretendard", "Noto Sans KR", sans-serif;
background-color: #f5f6fa;
margin: 0;
padding: 40px;
/*todo list들이 세로로 배열되도록*/
display: flex;
flex-direction: column;
align-items: center;
}

#title {
color: #636161;
font-size: 28px;
margin-bottom: 20px;
cursor: pointer;
}
.inputDiv {
width: 320px;
}

#todoInput {
padding: 7px 10px;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 8px;
}

#todoInput:focus {
border-color: #4a90e2;
}

#addBtn {
margin-left: 4px;
padding: 5px 10px;
font-size: 16px;
background-color: #4a90e2;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
}

#addBtn:hover {
background-color: #357abd;
}
#countDisplay {
width: 30px;
margin-left: 10px;
font-size: large;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금 inputDiv가 countDisplay div까지 포함되어 있는 상태라,
할 일 수가 0이거나 전체 보기를 누르면, 중앙 정렬이 안되는 거 같습니다!

Image

이 부분을 수정하려면

#countDisplay.hidden {
  display: none;
}

위 코드를 추가하시고,
할 일 갯수가 0이 되면 해당 클래스가 추가되도록, js에서도 수정해보시는 것도 좋을 것 같습니다!

/* 리스트 */
#todoList {
margin-top: 20px;
padding: 0;
}

#todoList li {
width: 320px;
display: flex;
justify-content: space-between;
align-items: center;
background: white;
margin: 6px 0;
padding: 12px 16px;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
font-size: 16px;
color: #444;
}

/* 삭제 버튼 */
.deleteBtn {
background-color: transparent;
color: #e74c3c;
border: none;
font-size: 14px;
cursor: pointer;
}

.deleteBtn:hover {
color: #c0392b;
}

#get-by-date {
margin-bottom: 50px;
}