Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Binary file added todo-app/asset/addButton.png
Copy link
Member

Choose a reason for hiding this comment

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

png보다는 svg를 사용하면, 벡터로 아이콘이 바뀌면서 색상을 수정할 수도 있고, 크기도 변화를 줄 수도 있습니다. 그외도 화질저하가 없다거나 다양한 장점이 있어서 아이콘 같은 asset파일에는 svg파일로 사용합니다!
-> 피그마에서 asset export해올 때 svg로 변경해서 다운 받으면 좋아요!

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added todo-app/asset/fonts/PretendardVariable.woff2
Binary file not shown.
Binary file added todo-app/asset/trash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions todo-app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<title>TO DO LIST</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="container">
<header>
<h1>투두 리스트</h1>
</header>

<main>
<section class="date">
<button id="prev">◀</button>
<p id="date"></p>
<button id="next">▶</button>
</section>

<section class="count">
<p id="counter"></p>
</section>

<section class="todo-block">
<ul id="todo-list" class="todo-list"></ul>
</section>
</main>

<footer class="todo-input">
<button id="add-btn">

Choose a reason for hiding this comment

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

todo list 추가 버튼이 왼쪽에 있고 + 모양이면, 처음 사용하는 입장에서 파일 추가 버튼이라고 생각할 수 도 있을 거 같습니다!
개인적으로 오른쪽에 배치하셨으면 더 좋겠다라는 맘이 듭니다...!

<img src="asset/addButton.png" />
</button>
<input
id="todo-input"
type="text"
placeholder="할 일을 입력해주세요. . !"
/>
</footer>
</div>
</body>
<script src="script.js"></script>
</html>
148 changes: 148 additions & 0 deletions todo-app/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
let currentDate = new Date(); //오늘 날짜 및 시간
let todosForEachDay = {}; //날짜별 투두를 저장할 배열

const addButton = document.getElementById("add-btn");
const todoInput = document.getElementById("todo-input");
const todoList = document.getElementById("todo-list");
const prevButton = document.getElementById("prev");
const nextButton = document.getElementById("next");

// 날짜를 키로 사용하기 위해서 포맷팅 필요
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}

//날짜 보여주기
function showDate() {
const dateElement = document.getElementById("date");

// 형식 변환
const options = {
year: "numeric",
month: "long",
day: "numeric",
weekday: "long",
};

dateElement.textContent = currentDate.toLocaleDateString("ko-KR", options);
}

// 투두 개수 보여주기 - 날짜 key에 해당하는 todo의 개수 중 completed 아닌 것만 골라서 개수
function Counter() {

Choose a reason for hiding this comment

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

일관성 있게 lowerCamelCase로 함수명을 counter로 하시거나, updateCounter가 더 적절할 거 같습니다!

const key = formatDate(currentDate);
const todos = todosForEachDay[key] || [];
const todoCount = todos.filter((todo) => !todo.completed).length;
const countTodos = document.getElementById("counter"); // 여기서 가져오기

Choose a reason for hiding this comment

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

const countTodos = document.getElementById("counter");
요거는 맨 위에 dom 요소 접근 변수 정의해둔 곳에 같이 적어두는게 더 낫지 않을까 제안드립니다!

if (countTodos) {
countTodos.textContent = `오늘의 남은 할 일은 ${todoCount}개 입니다 !`;
}
}

//투두 요소
function showTodos() {
todoList.innerHTML = "";
const key = formatDate(currentDate); //키: 날짜
const todos = todosForEachDay[key] || []; //투두요소: 각 키에 할당되는 요소들

todos.forEach((todo) => {
todoList.appendChild(createTodoElement(todo, key)); //투두 만들어지면 append
});
Counter();
}

// 투두 요소 만들기 (내용 및 완료, 삭제)
function createTodoElement(todo, key) {
const li = document.createElement("li");

//내용
const todoText = document.createElement("span");
todoText.className = "todo-text";
todoText.textContent = todo.text;

//완료 버튼
const completeButton = document.createElement("button");
completeButton.textContent = " ";
completeButton.className = "complete-btn";
completeButton.addEventListener("click", () => {
li.classList.toggle("completed");
todo.completed = !todo.completed;
saveTodos();
Counter();
});

//삭제 버튼
const deleteButton = document.createElement("button");
deleteButton.className = "delete-btn";
const deleteIcon = document.createElement("img");
deleteIcon.src = "asset/trash.png";
deleteIcon.width = 18;
deleteIcon.height = 18;
deleteButton.appendChild(deleteIcon);
deleteButton.addEventListener("click", () => {
todosForEachDay[key] = todosForEachDay[key].filter((t) => t.id !== todo.id);
saveTodos();
showTodos();
});

//li에 투두요소 추가
li.append(completeButton, todoText, deleteButton);
//완료 상태 보여주기
if (todo.completed) {
li.classList.add("completed");
}
return li;
}

//투두 추가
function addTodo() {
const text = todoInput.value.trim(); //입력받은 텍스트 공백 제거 후 text에 저장
if (text === "") return;

// 오늘 날짜에 해당하는 키에 투두 추가
const key = formatDate(currentDate);
if (!todosForEachDay[key]) todosForEachDay[key] = [];

// 삭제를 위한 id, 완료 여부 저장을 위한 completed 추가
const todo = { id: Date.now(), text, completed: false };
todosForEachDay[key].push(todo);

saveTodos();
showTodos();
todoInput.value = "";
Counter();
}
Comment on lines +100 to +116
Copy link
Member

Choose a reason for hiding this comment

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

https://developer.mozilla.org/ko/docs/Web/API/KeyboardEvent/key
addeventListner를 사용하여 enter키를 keydown한 경우 addTodo가 작동되도록 코드를 추가하면 UX 적으로 더 좋을 듯 합니다!


// 날짜 이동 버튼 및 투두와 날짜 재랜더링
prevButton.addEventListener("click", () => {
currentDate.setDate(currentDate.getDate() - 1);
showDate();
showTodos();
});
nextButton.addEventListener("click", () => {
currentDate.setDate(currentDate.getDate() + 1);
showDate();
showTodos();
});

//로컬스토리지 저장 및 불러오기
Copy link

@sungahChooo sungahChooo Sep 15, 2025

Choose a reason for hiding this comment

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

코드가 기능별로 정리되어있고 주석까지 잘 달려있어서 한눈에 보기 좋은 것 같아요!

function saveTodos() {
// 배열 문자열 변환 후 저장
localStorage.setItem("todosForEachDay", JSON.stringify(todosForEachDay));
}

function loadTodos() {
const data = localStorage.getItem("todosForEachDay");
if (data) {
// JSON 데이터 parsing -> 원래 배열로
todosForEachDay = JSON.parse(data);
}
}

//실행 시
addButton.addEventListener("click", addTodo);
loadTodos();
showDate();
showTodos();
149 changes: 149 additions & 0 deletions todo-app/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
@font-face {
font-family: "Pretendard Variable";
src: url("asset/fonts/PretendardVariable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
}

body {
justify-content: center;
font-family: "Pretendard Variable", sans-serif;
font-weight: 400;
background-color: #ffd7e9;
color: #202020;
}

.container {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
padding: 10px;
font-size: large;
transition: margin-left 0.3s;
}

.date {
justify-content: center;
}

.count {
text-align: center;
font-weight: 300;
font-size: medium;
}

.todo-block {
flex: 1;
width: 100%;
max-height: calc(100vh - 250px);
overflow-y: auto;
margin-bottom: 60px;
}

.date {
display: flex;
}

.date button {
background-color: transparent;
border: 0;
outline: none;
}

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

.todo-list li {

Choose a reason for hiding this comment

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

만들어진 todo list에 hover할때마다 삭제 아이콘이 옆에 생기면서, 리스트가 아래로 내려갔다 올라갔다 하는거 같습니다!

여기에 min-height: 30px; 정도 추가하시거나, img 사이즈를 15x15 정도로 줄이시는게 좋을 거 같습니다!

display: flex;
align-items: center;
gap: 10px;
margin-bottom: 5px;
}

.todo-list li .complete-btn {
order: -1;
border: none;
width: 20px;
height: 20px;
background-color: #f0f0f0;
border-radius: 50%;
cursor: pointer;
transition: background-color 0.3s;
position: relative;
}
Comment on lines +66 to +76
Copy link
Member

Choose a reason for hiding this comment

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

`<img width="1451" height="124" alt="스크린샷 2025-09-15 오후 4 09 44" src="https://github.com/user-attachments/assets/00aafd7d-05af-4a50-83da-74e0dfa6939e" />`

텍스트가 길어지면 다음과 같이 complete-btn도 길어지는데, flex: 0 0 20px; 다음 속성을 추가하면 20px로 고정시킬 수 있습니다


.todo-list li.completed .complete-btn {
background-color: #eb89b5;
}

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

.todo-list li.completed .complete-btn::after {
content: "✔";
color: #fffef1;
font-size: 14px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

.todo-list li .delete-btn {
display: none;
margin-left: auto;
border: none;
background: transparent;
cursor: pointer;
font-size: 16px;
}

.todo-list li:hover .delete-btn {
display: inline-block;
}

.todo-input {
display: flex;
align-items: center;
gap: 10px;
background-color: #fffef1;
position: fixed;
bottom: 10px;
padding-top: 4px;
height: 40px;
width: calc(90vw - 100px);

Choose a reason for hiding this comment

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

width: calc(90vw - 100px);
모니터 전체화면으로 보면 너무 긴거 같아서, 2주차 리팩토링 하실 때, 모니터 사이즈랑 모바일 사이즈 정도로 나눠서 반응형으로 하시는게 좋을 거 같습니다! 모바일 사이즈에서 가로 길이는 괜찮은 거 같아요!
그리고 입력창이 너무 하단에 붙어 있는 거 같아서 좀 더 위로 올려주셔도 괜찮을거 같습니다!

border-radius: 30px;
}
Comment on lines +110 to +121
Copy link
Member

Choose a reason for hiding this comment

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

스크린샷 2025-09-15 오후 4 06 39 스크린샷 2025-09-15 오후 4 07 13 이렇게 속성 추가하면, 오른쪽 끝에 붙는게 있어서 padding 속성을 좌우로 줘도 좋을 듯 보여요


#todo-input {
background-color: transparent;
height: 32px;
font-size: 15px;
border: 0;
outline: none;
}
Comment on lines +123 to +129
Copy link
Member

Choose a reason for hiding this comment

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

스크린샷 2025-09-15 오후 3 53 22 지금 type="text"로 지정되어있어서 텍스트의 입력값이 placeholder만큼만 지정되어있습니다. `flex: 1; min-width:0;`를 추가해주시면, 텍스트 입력창이 넓어질 거에요!


#add-btn {
background-color: transparent;
border: 0;
outline: none;
}

#add-btn img {
width: 34px;
height: 34px;
object-fit: contain;
}

#add-btn:hover {
transform: scale(1.05);
}

#add-btn:active {
transform: scale(0.9);
}