-
Notifications
You must be signed in to change notification settings - Fork 10
[1주차] 이채연 과제 제출합니다. #2
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: main
Are you sure you want to change the base?
Changes from all commits
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,8 @@ | ||
| { | ||
| "semi": true, | ||
| "trailingComma": "es5", | ||
| "singleQuote": true, | ||
| "printWidth": 80, | ||
| "tabWidth": 2, | ||
| "useTabs": false | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,261 @@ | ||
| // 전역 변수 | ||
| let todosByDate = {}; | ||
| let currentDate = new Date(); | ||
| let nextId = 1; | ||
|
Member
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. 지금 |
||
|
|
||
| // 페이지 로드 완료 시 실행 | ||
| window.addEventListener('load', function () { | ||
| // 기존 데이터 삭제 (새 구조로 변경) | ||
| localStorage.removeItem('todoList'); | ||
|
Member
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. 이 부분은 지우셔도 될 것 같아요 |
||
|
|
||
| initializeApp(); | ||
| this.getSelection; | ||
|
Member
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. 이 코드도 현재로써는 동작하지 않는 코드인 거 같아요 |
||
| }); | ||
|
|
||
| // 앱 초기화 | ||
| function initializeApp() { | ||
| loadTodosFromStorage(); | ||
| updateDateDisplay(); | ||
| setupEventListeners(); | ||
| renderTodoList(); | ||
| updateProgress(); | ||
| } | ||
|
|
||
| // 이벤트 리스너 설정 | ||
| function setupEventListeners() { | ||
| // 이전 날짜 버튼 | ||
| const prevBtn = document.getElementById('prevDay'); | ||
| if (prevBtn) { | ||
| prevBtn.onclick = function () { | ||
| moveToDate(-1); | ||
| }; | ||
| } | ||
|
|
||
| // 다음 날짜 버튼 | ||
| const nextBtn = document.getElementById('nextDay'); | ||
| if (nextBtn) { | ||
| nextBtn.onclick = function () { | ||
| moveToDate(1); | ||
| }; | ||
| } | ||
|
|
||
| // 날짜 입력 필드 | ||
| const dateInput = document.getElementById('dateInput'); | ||
| if (dateInput) { | ||
| dateInput.onchange = function () { | ||
| selectDate(this.value); | ||
| }; | ||
| } | ||
|
|
||
| // 할일 추가 폼 | ||
| const todoForm = document.getElementById('todoForm'); | ||
| if (todoForm) { | ||
| todoForm.onsubmit = function (e) { | ||
| e.preventDefault(); | ||
| const input = document.getElementById('todoInput'); | ||
| const text = input.value.trim(); | ||
| if (text) { | ||
| addTodo(text); | ||
| input.value = ''; | ||
| } else { | ||
| alert('할일을 입력해주세요!'); | ||
| } | ||
|
Member
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. 지금 input tag에 이미 required 속성이 존재하여 alert가 추가적으로 동작하지 않는 것 같아요. 해당 부분 확인해 주시면 좋을 거 같아요 |
||
| }; | ||
| } | ||
| } | ||
|
|
||
| // 날짜 이동 (-1: 어제, 1: 내일) | ||
| function moveToDate(direction) { | ||
| currentDate.setDate(currentDate.getDate() + direction); | ||
| updateDateDisplay(); | ||
| renderTodoList(); | ||
| updateProgress(); | ||
| } | ||
|
Comment on lines
+68
to
+73
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. eventListener를 통해 moveToDate 코드를 이전,이후에 삽입하는 방식 대신 하나의 함수화를 통해 더 깔끔한 코드인 것 같습니다 |
||
|
|
||
| // 특정 날짜 선택 | ||
| function selectDate(dateString) { | ||
| const newDate = new Date(dateString + 'T00:00:00'); | ||
| if (!isNaN(newDate.getTime())) { | ||
| currentDate = newDate; | ||
| updateDateDisplay(); | ||
| renderTodoList(); | ||
| updateProgress(); | ||
| } | ||
| } | ||
|
|
||
| // 현재 날짜 문자열 반환 | ||
| function getCurrentDateString() { | ||
| const year = currentDate.getFullYear(); | ||
| const month = String(currentDate.getMonth() + 1).padStart(2, '0'); | ||
| const day = String(currentDate.getDate()).padStart(2, '0'); | ||
| return `${year}-${month}-${day}`; | ||
| } | ||
|
|
||
| // 날짜 표시 업데이트 | ||
| function updateDateDisplay() { | ||
| const dateInfo = document.getElementById('dateInfo'); | ||
| const dateInput = document.getElementById('dateInput'); | ||
|
|
||
| if (!dateInfo || !dateInput) return; | ||
|
|
||
| const year = currentDate.getFullYear(); | ||
| const month = currentDate.getMonth() + 1; | ||
| const date = currentDate.getDate(); | ||
|
|
||
| const days = [ | ||
| '일요일', | ||
| '월요일', | ||
| '화요일', | ||
| '수요일', | ||
| '목요일', | ||
| '금요일', | ||
| '토요일', | ||
| ]; | ||
| const dayName = days[currentDate.getDay()]; | ||
|
|
||
| const displayText = `${year}년 ${month}월 ${date}일 ${dayName}`; | ||
| const dateValue = getCurrentDateString(); | ||
|
|
||
| dateInfo.textContent = displayText; | ||
| dateInput.value = dateValue; | ||
| } | ||
|
|
||
| // 현재 날짜의 할일 목록 가져오기 | ||
| function getCurrentTodoList() { | ||
| const dateStr = getCurrentDateString(); | ||
| if (!todosByDate[dateStr]) { | ||
| todosByDate[dateStr] = []; | ||
| } | ||
| return todosByDate[dateStr]; | ||
| } | ||
|
|
||
| // 할일 추가 | ||
| function addTodo(text) { | ||
| const newTodo = { | ||
| id: nextId++, | ||
| text: text, | ||
| completed: false, | ||
| date: getCurrentDateString(), | ||
| }; | ||
|
Comment on lines
+134
to
+139
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. newTodo로 한번에 push할 수 있게 한 부분이 인상깊었습니다 |
||
|
|
||
| const todos = getCurrentTodoList(); | ||
| todos.push(newTodo); | ||
|
|
||
| saveTodosToStorage(); | ||
| renderTodoList(); | ||
| updateProgress(); | ||
| } | ||
|
|
||
| // 할일 완료 토글 | ||
| function toggleTodo(id) { | ||
| const todos = getCurrentTodoList(); | ||
| const todo = todos.find((t) => t.id === id); | ||
|
|
||
| if (todo) { | ||
| todo.completed = !todo.completed; | ||
| saveTodosToStorage(); | ||
| renderTodoList(); | ||
| updateProgress(); | ||
| } | ||
| } | ||
|
|
||
| // 할일 삭제 | ||
| function deleteTodo(id) { | ||
| const dateStr = getCurrentDateString(); | ||
| const todos = getCurrentTodoList(); | ||
| const filteredTodos = todos.filter((t) => t.id !== id); | ||
|
|
||
| todosByDate[dateStr] = filteredTodos; | ||
|
|
||
| saveTodosToStorage(); | ||
| renderTodoList(); | ||
| updateProgress(); | ||
| } | ||
|
Member
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가 없을 때 empty list로 유지하는 대신 아예 삭제하는 것도 데이터 관리/최적화 측면에서 고려해볼만한 선택인 거 같아요 |
||
|
|
||
| // 할일 목록 렌더링 | ||
| function renderTodoList() { | ||
| const todoList = document.getElementById('todoList'); | ||
| const emptyState = document.getElementById('emptyState'); | ||
|
|
||
| if (!todoList) return; | ||
|
|
||
| const todos = getCurrentTodoList(); | ||
|
|
||
| // 기존 목록 지우기 | ||
| todoList.innerHTML = ''; | ||
|
|
||
| if (todos.length === 0) { | ||
| if (emptyState) emptyState.style.display = 'block'; | ||
| return; | ||
| } | ||
|
|
||
| if (emptyState) emptyState.style.display = 'none'; | ||
|
|
||
| // 할일 항목들 생성 | ||
| todos.forEach((todo) => { | ||
| const li = document.createElement('li'); | ||
| li.className = `todoItem ${todo.completed ? 'completed' : ''}`; | ||
|
|
||
| li.innerHTML = ` | ||
| <label class="todoLabel"> | ||
| <input type="checkbox" class="todoCheckbox" ${ | ||
| todo.completed ? 'checked' : '' | ||
| } | ||
| onchange="toggleTodo(${todo.id})"> | ||
| <span class="todoText">${todo.text}</span> | ||
| </label> | ||
| <button class="deleteBtn" onclick="deleteTodo(${todo.id})">🗑️</button> | ||
| `; | ||
|
Comment on lines
+199
to
+208
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. 제가 찾아본 바로는 이 부분이 XSS 공격 위험이 있다고 합니다
Author
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 사용은 위험할수잇으니까 최소화해야함 |
||
|
|
||
| todoList.appendChild(li); | ||
| }); | ||
| } | ||
|
|
||
| // 진행상황 업데이트 | ||
| function updateProgress() { | ||
| const progressText = document.getElementById('progressText'); | ||
|
|
||
| if (!progressText) return; | ||
|
|
||
| const todos = getCurrentTodoList(); | ||
| const totalCount = todos.length; | ||
| const completedCount = todos.filter((t) => t.completed).length; | ||
|
|
||
| const dateStr = getCurrentDateString(); | ||
| const today = new Date().toISOString().split('T')[0]; | ||
| const isToday = dateStr === today; | ||
| const prefix = isToday ? '오늘' : dateStr; | ||
|
Member
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. 이렇게 오늘 케이스 구분해 놓은 거 좋은 거 같아요 |
||
|
|
||
| const text = `${prefix} 할일 ${totalCount}개 중 ${completedCount}개 완료`; | ||
| progressText.textContent = text; | ||
| } | ||
|
|
||
| // 데이터 저장 | ||
| function saveTodosToStorage() { | ||
| try { | ||
| localStorage.setItem('todosByDate', JSON.stringify(todosByDate)); | ||
| localStorage.setItem('nextId', nextId.toString()); | ||
| } catch (error) { | ||
| console.error('저장 오류:', error); | ||
| } | ||
| } | ||
|
|
||
| // 데이터 불러오기 | ||
| function loadTodosFromStorage() { | ||
| try { | ||
| const saved = localStorage.getItem('todosByDate'); | ||
| const savedId = localStorage.getItem('nextId'); | ||
|
|
||
| if (saved) { | ||
| todosByDate = JSON.parse(saved); | ||
| } | ||
|
|
||
| if (savedId) { | ||
| nextId = parseInt(savedId); | ||
| } | ||
| } catch (error) { | ||
| console.error('불러오기 오류:', error); | ||
| todosByDate = {}; | ||
| nextId = 1; | ||
| } | ||
|
Comment on lines
+245
to
+260
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. 불러오는 과정에서의 exception 처리는 좋은 습관인것 같습니다 |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="ko"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>todo list</title> | ||
| <link rel="stylesheet" href="style.css" /> | ||
| </head> | ||
|
|
||
| <body> | ||
| <main class="appContainer"> | ||
| <header class="appHeader"> | ||
| <h1 class="appTitle">✔️TO DO LIST✔️</h1> | ||
| <div class="dateSelector"> | ||
| <button class="dateNavBtn" id="prevDay">◀</button> | ||
| <div class="dateDisplay"> | ||
| <input type="date" class="dateInput" id="dateInput" /> | ||
| <p class="dateInfo" id="dateInfo">2024년 12월 19일 목요일</p> | ||
| </div> | ||
| <button class="dateNavBtn" id="nextDay">▶</button> | ||
|
Member
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 property 중
Member
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. 지금 일부 버튼에만 적용되어 있네요 (e.g., |
||
| </div> | ||
| </header> | ||
|
|
||
| <section class="todoInputSection"> | ||
| <form class="todoForm" id="todoForm"> | ||
| <input | ||
| type="text" | ||
| class="todoInput" | ||
| id="todoInput" | ||
| placeholder="할일을 입력하세요" | ||
| maxlength="100" | ||
| required | ||
|
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. required 가 경고 parameter라는 것을 알게 됐습니다 |
||
| /> | ||
| <button type="submit" class="addButton">➕ 추가</button> | ||
| </form> | ||
| </section> | ||
|
|
||
| <section class="todoProgress"> | ||
| <p class="progressText" id="progressText">오늘 할일 0개 중 0개 완료</p> | ||
| </section> | ||
|
|
||
| <section class="todoListSection"> | ||
| <ul class="todoList" id="todoList"></ul> | ||
| <div class="emptyState" id="emptyState"> | ||
| <p>📝 할일을 추가해보세요 📝</p> | ||
| </div> | ||
| </section> | ||
| </main> | ||
|
|
||
| <script src="app.js"></script> | ||
| </body> | ||
| </html> | ||
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.
prettier 사용하신 거 좋은 거 같아요. 제가 사용하는 세팅도 공유드릴테니 해당 부분도 보시면 좋을 거 같아요