-
Notifications
You must be signed in to change notification settings - Fork 8
[1주차] 오유진 과제 제출합니다. #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import { todoActions, todos, currentDate } from "./todoLogic.js"; | ||
|
|
||
| const dateElement = document.getElementById("today-date"); | ||
| const prevBtn = document.getElementById("prev-day"); | ||
| const nextBtn = document.getElementById("next-day"); | ||
| const todoForm = document.getElementById("todo-form"); | ||
| const todoInput = document.getElementById("todo-input"); | ||
| const todoList = document.getElementById("todo-list"); | ||
| const todoCount = document.getElementById("todo-count"); | ||
|
|
||
| //날짜 | ||
| function formatDate(date) { | ||
| return `${date.getFullYear()}년 ${date.getMonth() + 1}월 ${date.getDate()}일`; | ||
| } | ||
|
|
||
| function getDateKey(date) { | ||
| return date.toISOString().split("T")[0]; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. toISOString()을 사용하면 key로 로컬 날짜가 아니라, UTC 기준 날짜 기반 문자열을 반환해서 로컬 날짜와 다른 키로 저장될 수 있어요. 현재는 잘 동작하지만, 일관성 측면에서 ISOString() 사용보다 getFullYear(), getMonth(), getDate()를 활용해 로컬 기준'YYYY-MM-DD' 문자열을 직접 생성하는 방식이 더 좋을 수도 있을 것 같아요. 브라우저 콘솔에 코드 입력해보시면 확인하실 수 있습니다! const testDate = new Date(2026, 2, 16, 1, 0, 0);
console.log("로컬:", testDate.toString());
console.log("ISO:", testDate.toISOString());
console.log("date key:", testDate.toISOString().split("T")[0]); |
||
| } | ||
|
|
||
| // 렌더링 | ||
| function render() { | ||
| //날짜 관련 | ||
| dateElement.innerText = formatDate(currentDate); | ||
| const currentKey = getDateKey(currentDate); | ||
| const filteredTodos = todos.filter((todo) => todo.date === currentKey); | ||
|
|
||
| todoList.innerHTML = ""; | ||
|
|
||
| // map 함수 느낌 | ||
| filteredTodos.forEach((todo) => { | ||
| const li = document.createElement("li"); | ||
| li.className = `todo-item ${todo.completed ? "completed" : ""}`; | ||
|
|
||
| li.innerHTML = ` | ||
| <label class="checkbox-container"> | ||
| <input type="checkbox" class="todo-checkbox" ${ | ||
| todo.completed ? "checked" : "" | ||
| }> | ||
| <span class="checkmark"></span> | ||
| </label> | ||
| <p class="todo-text">${todo.text}</p> | ||
| <button class="delete-button">삭제</button> | ||
| `; | ||
|
Comment on lines
+34
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. innerHTML 사용이 적절할지? 에 대한 고민을 해보실 수 있는 링크 첨부해드립니당! https://github.com/CEOS-Developers/vanilla-todo-21th/pull/6/files#r1997513080
Comment on lines
+34
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. todo.text는 html input 태그에서 가져오는 사용자 입력값인데, innerHTML에 그대로 삽입하기보다는 textContent로 넣는 게 더 안전해보여요. 문서에서도 innerHTML은 문자열을 HTML로 그대로 파싱하는 API라서 injection 공격에 취약하다고 적혀있어요. 예를 들어, 사용자가 '<b>코드 리뷰하기</b>'라고 입력하면 Bold체로 할 일이 등록되는 것을 확인하실 수 있을 거에요.
|
||
|
|
||
| // 체크박스 클릭 이벤트 | ||
| li.querySelector(".todo-checkbox").addEventListener("change", () => { | ||
| todoActions.toggleTodo(todo.id); | ||
| render(); | ||
| }); | ||
|
|
||
| // 삭제 버튼 클릭 이벤트 | ||
| li.querySelector(".delete-button").addEventListener("click", () => { | ||
| todoActions.deleteTodo(todo.id); | ||
| render(); | ||
| }); | ||
|
|
||
| todoList.appendChild(li); | ||
| }); | ||
|
|
||
| const remaining = filteredTodos.filter((todo) => !todo.completed).length; | ||
| todoCount.innerText = `${remaining}개`; | ||
| } | ||
|
|
||
| // 날짜 이동 | ||
| prevBtn.addEventListener("click", () => { | ||
| todoActions.changeDate(-1); // 전날 | ||
| render(); | ||
| }); | ||
|
|
||
| nextBtn.addEventListener("click", () => { | ||
| todoActions.changeDate(1); // 다음날 | ||
| render(); | ||
| }); | ||
|
|
||
| //투두 등록 | ||
| todoForm.addEventListener("submit", (e) => { | ||
| e.preventDefault(); | ||
| const text = todoInput.value.trim(); | ||
| if (text) { | ||
| todoActions.addTodo(text, getDateKey(currentDate)); | ||
| todoInput.value = ""; | ||
| render(); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 입력값 앞뒤 공백을 제거하고 빈 문자열을 한 번 더 체크해주신 점이 좋은 것 같습니다! |
||
| }); | ||
|
|
||
| render(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="kor"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <link rel="stylesheet" href="style.css" /> | ||
| <title>TodoList</title> | ||
| </head> | ||
| <body> | ||
| <header> | ||
| <span>To Do</span> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To Do는 페이지의 대표 제목이니, span보다는 h1 같은 heading 태그를 사용하는 것도 좋을 것 같아요. |
||
| <div class="date-controller"> | ||
| <button id="prev-day">◀</button> | ||
| <time id="today-date">0000년 00월 00일</time> | ||
| <button id="next-day">▶</button> | ||
| </div> | ||
| </header> | ||
| <main> | ||
| <form id="todo-form"> | ||
| <input | ||
| type="text" | ||
| id="todo-input" | ||
| placeholder="오늘의 할일을 적어주세요!" | ||
| required | ||
| /> | ||
| <div> | ||
| <span id="todo-count">0개</span> | ||
| <button type="submit" id="submit-button">등록</button> | ||
| </div> | ||
| </form> | ||
| <ul id="todo-list"></ul> | ||
| </main> | ||
|
|
||
| <script src="app.js" type="module"></script> | ||
| </body> | ||
| </html> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2주차때는 pretendard 폰트도 사용해보셨으면 합니당~ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| /* body */ | ||
| body { | ||
| background-color: #9bbbd4; | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| } | ||
|
|
||
| /* header */ | ||
| header { | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| padding: 20px; | ||
| gap: 20px; | ||
| } | ||
|
|
||
| header span { | ||
| font-size: 32px; | ||
| font-weight: bold; | ||
| color: #3a1d1d; | ||
| margin-bottom: 20px; | ||
| } | ||
|
|
||
| .date-controller { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| gap: 20px; | ||
| } | ||
|
Comment on lines
+25
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 민교님 리뷰 코멘트 |
||
|
|
||
| #today-date { | ||
| font-size: 20px; | ||
| font-weight: 600; | ||
| color: #3a1d1d; | ||
| } | ||
|
|
||
| .date-controller button { | ||
| background: none; | ||
| border: none; | ||
| font-size: 20px; | ||
| cursor: pointer; | ||
| color: #3a1d1d; | ||
| transition: transform 0.2s; | ||
| } | ||
|
|
||
| .date-controller button:hover { | ||
| transform: scale(1.2); | ||
| } | ||
|
|
||
| /* todo form */ | ||
| #todo-form { | ||
| display: flex; | ||
| align-items: center; | ||
| background: white; | ||
| border-radius: 15px; | ||
| padding: 10px 20px; | ||
| gap: 20px; | ||
| width: 300px; | ||
| box-sizing: border-box; | ||
| margin: 0 auto; | ||
| } | ||
|
|
||
| /* todo input */ | ||
| #todo-input { | ||
| flex-grow: 1; | ||
| border: 0; | ||
| } | ||
|
|
||
| #submit-button { | ||
| background-color: #f7e600; | ||
| color: #3a1d1d; | ||
| border: 0; | ||
| border-radius: 10px; | ||
| padding: 5px 10px; | ||
| font-weight: 500; | ||
| } | ||
|
Comment on lines
+70
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기에도 cursor: pointer; 추가해주시면 좋을거 같아용! 추가로 deleteButton 처럼 hover시에 효과도 넣어주면 좋을거 같습니다~ |
||
|
|
||
| /* todo item */ | ||
| #todo-list { | ||
| padding: 0; | ||
| margin: 0; | ||
| list-style: none; | ||
| } | ||
| /* ul 안의 li들은 기본적으로 paddiing left 40px을 가짐.. 이걸 없애줘야 가운데 정렬됨 */ | ||
|
|
||
| .todo-item { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| background-color: #f7e600; | ||
| color: #3a1d1d; | ||
| border-radius: 15px; | ||
| margin-top: 15px; | ||
| padding: 10px; | ||
| width: 300px; | ||
| box-sizing: border-box; | ||
| } | ||
|
|
||
| .todo-item.completed .todo-text { | ||
| text-decoration: line-through; | ||
| color: gray; | ||
| } | ||
|
|
||
| .todo-checkbox { | ||
| display: none; | ||
| } | ||
|
|
||
| .checkmark { | ||
| width: 25px; | ||
| height: 25px; | ||
| background-color: white; | ||
| border-radius: 8px; | ||
| display: inline-flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| cursor: pointer; | ||
| margin-right: 15px; | ||
| position: relative; | ||
| } | ||
|
|
||
| .todo-checkbox:checked + .checkmark { | ||
| background-color: #5bd15f; | ||
| } | ||
|
|
||
| .todo-checkbox:checked + .checkmark::after { | ||
| content: "✔"; | ||
| color: white; | ||
| font-size: 16px; | ||
| font-weight: bold; | ||
| } | ||
|
|
||
| .todo-text { | ||
| flex-grow: 1; | ||
| text-align: center; | ||
| margin: 0 10px; | ||
| word-break: break-all; | ||
| } | ||
|
|
||
| .delete-button { | ||
| background-color: #3a1d1d; | ||
| color: #f7e600; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 색상 값이 여러 군데 반복되고 있어서, 이후 스타일 수정 시 한 번에 관리하기 쉽도록 CSS 변수로 빼두는 것도 좋을 것 같아요! |
||
| border: none; | ||
| border-radius: 12px; | ||
| padding: 8px 15px; | ||
| cursor: pointer; | ||
| font-weight: bold; | ||
| } | ||
|
Comment on lines
+140
to
+148
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| .delete-button:hover { | ||
| background-color: black; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| export let todos = []; | ||
| export let currentDate = new Date(); | ||
|
|
||
| export const todoActions = { | ||
| //날짜 변경 | ||
| changeDate: (offset) => { | ||
| currentDate.setDate(currentDate.getDate() + offset); | ||
| return currentDate; | ||
| }, | ||
|
|
||
| // 투두 추가 | ||
| addTodo: (text, dateStr) => { | ||
| const newTodo = { | ||
| id: Date.now(), | ||
| text: text, | ||
| completed: false, | ||
| date: dateStr, | ||
| }; | ||
| todos = [...todos, newTodo]; | ||
| return todos; | ||
| }, | ||
|
|
||
| deleteTodo: (id) => { | ||
| todos = todos.filter((todo) => todo.id !== id); | ||
| return todos; | ||
| }, | ||
|
|
||
| toggleTodo: (id) => { | ||
| todos = todos.map((todo) => | ||
| todo.id === id ? { ...todo, completed: !todo.completed } : todo | ||
| ); | ||
| return todos; | ||
| }, | ||
| }; |


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2주차 react 마이그레이션 과정에서는 localStorage 한번 활용해보셨으면 합니당!