-
Notifications
You must be signed in to change notification settings - Fork 10
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
[1주차] 김현민 미션 제출합니다. #2
base: master
Are you sure you want to change the base?
Conversation
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.
안녕하세요 프론트 운영진 배성준입니다~~!!!!!!!!!!!!!!!!!!
첫 과제 너무 고생하셨어요~~ trim도 사용하시고 반응형도 고려하신 것 같아 되게 꼼꼼하신것같습니다~~ media query나 rem을 자유자재로 잘 사용하시는 것 같아 부러워요 .. 배워갑니다
제가 약간 코멘트 달았는데 참고해주시면 될 것 같아요!! 고생하셨습니다~~
font-weight: 500; | ||
font-weight: 600; |
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.
두개 합치면 1100 !?!!?!?!?!?!
addTodo.addEventListener("click", () => { | ||
const text = todoInput.value.trim(); //입력한 todo의 불필요한 공백 제거 | ||
if (text) { | ||
const newTodo = { text, checked: false }; //입력된 text와, 초기에 check은 false이므로 해당 상태를 변수로 선언 | ||
storedTodos.push(newTodo); //현재 todo를 저장하는 배열 변수에 push | ||
saveTodos(); //push된 배열 변수의 상태를 localStorage에 update | ||
addTodoItem(newTodo); //현재 입력한 todo의 상태를 화면에 보여주도록 선언한 addTodoItem에 전달 | ||
todoInput.value = ""; //입력을 완료하면 현재 input 값 빈 string으로 선언 | ||
} | ||
}); | ||
|
||
//PC에서의 편리한 사용을 고려하여 엔터를 눌러도 해당 함수가 trigger 되게 addEventListener 등록 | ||
todoInput.addEventListener("keydown", (event) => { | ||
if (event.key === "Enter") { | ||
const text = todoInput.value.trim(); | ||
if (text) { | ||
const newTodo = { text, checked: false }; | ||
storedTodos.push(newTodo); | ||
saveTodos(); | ||
addTodoItem(newTodo); | ||
todoInput.value = ""; | ||
} | ||
} | ||
}); |
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.
이벤트리스너의 콜백함수를 따로 빼서 관리해도 좋을 것 같아요!
addTodo.addEventListener("click", () => { | |
const text = todoInput.value.trim(); //입력한 todo의 불필요한 공백 제거 | |
if (text) { | |
const newTodo = { text, checked: false }; //입력된 text와, 초기에 check은 false이므로 해당 상태를 변수로 선언 | |
storedTodos.push(newTodo); //현재 todo를 저장하는 배열 변수에 push | |
saveTodos(); //push된 배열 변수의 상태를 localStorage에 update | |
addTodoItem(newTodo); //현재 입력한 todo의 상태를 화면에 보여주도록 선언한 addTodoItem에 전달 | |
todoInput.value = ""; //입력을 완료하면 현재 input 값 빈 string으로 선언 | |
} | |
}); | |
//PC에서의 편리한 사용을 고려하여 엔터를 눌러도 해당 함수가 trigger 되게 addEventListener 등록 | |
todoInput.addEventListener("keydown", (event) => { | |
if (event.key === "Enter") { | |
const text = todoInput.value.trim(); | |
if (text) { | |
const newTodo = { text, checked: false }; | |
storedTodos.push(newTodo); | |
saveTodos(); | |
addTodoItem(newTodo); | |
todoInput.value = ""; | |
} | |
} | |
}); | |
addButton.addEventListener("click", handleAddTodo); | |
inputElement.addEventListener("keydown", (event) => { | |
if (event.key === "Enter") { | |
handleAddTodo(); | |
} | |
}); | |
function handleAddTodo() { | |
const text = inputElement.value.trim(); | |
if (text) { | |
const newTodo = { text, checked: false }; | |
storedTodos.push(newTodo); | |
saveTodos(); | |
addTodoItem(newTodo); | |
inputElement.value = ""; | |
} | |
} |
.deleteBtn { | ||
background-color: transparent; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
margin-left: 1rem; | ||
border: none; | ||
font-size: 4rem; | ||
color: gray; | ||
} |
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.
유저가 클릭해서 action을 취할 수 있는 부분에 대해서는 cursor : pointer
을 주는 것도 좋을 것 같아요
todoItem.appendChild(checkbox); | ||
todoItem.appendChild(todoText); | ||
todoItem.appendChild(deleteButton); |
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.
appendChild를 append를 이용하여 작성할 수도 있어요! 한번 블로그글 이것도 참고해보면 좋을 것 같아요
function saveTodos() { | ||
localStorage.setItem("todos", JSON.stringify(storedTodos)); | ||
} |
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.
선택사항인 로컬스토리지도 구현하셨네요!!!!!!!!!!!!!!!!!!!! 굿입니다
function addTodoItem(todo) { | ||
const todoItem = document.createElement("div"); | ||
todoItem.classList.add("todoWrapper"); | ||
|
||
const checkbox = document.createElement("input"); | ||
checkbox.type = "checkbox"; | ||
checkbox.classList.add("todoCheckbox"); | ||
checkbox.checked = todo.checked || false; //전달받은 todo의 check 상태가 true면 true, 아니면 false로 선언 | ||
checkbox.addEventListener("change", () => { | ||
todo.checked = checkbox.checked; //해당 todo의 체크박스 상태가 변하면 그 상태를 저장해 준 후 | ||
saveTodos(); //localStorage의 체크박스 상태 변화 저장 | ||
todoText.style.textDecoration = checkbox.checked ? "line-through" : "none"; //체크박스가 check 되어있다면 해당 todo에 줄 그어줌 | ||
updateCheckedTodoCount(); //해당 todo의 체크 상태가 변했으므로 진행중인 todo와 완료된 todo개수 update | ||
}); | ||
|
||
const todoText = document.createElement("div"); | ||
todoText.textContent = todo.text; //불러온 todo의 text 값을 생성한 요소에 저장 | ||
todoText.classList.add("todoText"); | ||
todoText.style.textDecoration = todo.checked ? "line-through" : "none"; //불러온 todo의 체크 상태에 따라 줄을 그을지 말지 판단 | ||
|
||
const deleteButton = document.createElement("button"); | ||
deleteButton.classList.add("deleteBtn"); | ||
deleteButton.textContent = "X"; | ||
//각 todo의 X 버튼을 누르면 trigger | ||
deleteButton.addEventListener("click", () => { | ||
const index = storedTodos.indexOf(todo); //전체 todo 상태가 저장되어 있는 배열에서 해당 todo의 index 값 불러옴 | ||
storedTodos.splice(index, 1); //해당 index부터 1개의 요소 삭제(즉, 해당 todo만 삭제) | ||
saveTodos(); //삭제된 상태를 localStorage 에 update | ||
todoList.removeChild(todoItem); //해당 todo를 화면에서 지워줌 | ||
updateTotalTodoCount(); //전체 todo 개수 update | ||
updateCheckedTodoCount(); //진행중 및 완료 todo 개수 update | ||
}); |
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.
addTodoItem 이라는 함수가 어떤 로직을 처리하는지 한 눈에 볼 수 있도록 각 checkbox,todoText,deleteButton을 만드는 함수를 따로 처리하면 가독성이나 유지보수 측면에서도 좋을 것 같아요!
function addTodoItem(todo) { | |
const todoItem = document.createElement("div"); | |
todoItem.classList.add("todoWrapper"); | |
const checkbox = document.createElement("input"); | |
checkbox.type = "checkbox"; | |
checkbox.classList.add("todoCheckbox"); | |
checkbox.checked = todo.checked || false; //전달받은 todo의 check 상태가 true면 true, 아니면 false로 선언 | |
checkbox.addEventListener("change", () => { | |
todo.checked = checkbox.checked; //해당 todo의 체크박스 상태가 변하면 그 상태를 저장해 준 후 | |
saveTodos(); //localStorage의 체크박스 상태 변화 저장 | |
todoText.style.textDecoration = checkbox.checked ? "line-through" : "none"; //체크박스가 check 되어있다면 해당 todo에 줄 그어줌 | |
updateCheckedTodoCount(); //해당 todo의 체크 상태가 변했으므로 진행중인 todo와 완료된 todo개수 update | |
}); | |
const todoText = document.createElement("div"); | |
todoText.textContent = todo.text; //불러온 todo의 text 값을 생성한 요소에 저장 | |
todoText.classList.add("todoText"); | |
todoText.style.textDecoration = todo.checked ? "line-through" : "none"; //불러온 todo의 체크 상태에 따라 줄을 그을지 말지 판단 | |
const deleteButton = document.createElement("button"); | |
deleteButton.classList.add("deleteBtn"); | |
deleteButton.textContent = "X"; | |
//각 todo의 X 버튼을 누르면 trigger | |
deleteButton.addEventListener("click", () => { | |
const index = storedTodos.indexOf(todo); //전체 todo 상태가 저장되어 있는 배열에서 해당 todo의 index 값 불러옴 | |
storedTodos.splice(index, 1); //해당 index부터 1개의 요소 삭제(즉, 해당 todo만 삭제) | |
saveTodos(); //삭제된 상태를 localStorage 에 update | |
todoList.removeChild(todoItem); //해당 todo를 화면에서 지워줌 | |
updateTotalTodoCount(); //전체 todo 개수 update | |
updateCheckedTodoCount(); //진행중 및 완료 todo 개수 update | |
}); | |
function createCheckbox(checked) { | |
const checkbox = document.createElement("input"); | |
checkbox.type = "checkbox"; | |
checkbox.classList.add("todoCheckbox"); | |
checkbox.checked = checked || false; | |
checkbox.addEventListener("change", handleCheckboxChange); | |
return checkbox; | |
} | |
function createTodoText(text, checked) { | |
const todoText = document.createElement("div"); | |
todoText.textContent = text; | |
todoText.classList.add("todoText"); | |
todoText.style.textDecoration = checked ? "line-through" : "none"; | |
return todoText; | |
} | |
function createDeleteButton(todo, todoItem) { | |
const deleteButton = document.createElement("button"); | |
deleteButton.classList.add("deleteBtn"); | |
deleteButton.textContent = "X"; | |
deleteButton.addEventListener("click", () => { | |
const index = storedTodos.indexOf(todo); //전체 todo 상태가 저장되어 있는 배열에서 해당 todo의 index 값 불러옴 | |
storedTodos.splice(index, 1); //해당 index부터 1개의 요소 삭제(즉, 해당 todo만 삭제) | |
saveTodos(); //삭제된 상태를 localStorage 에 update | |
todoList.removeChild(todoItem); //해당 todo를 화면에서 지워줌 | |
updateTotalTodoCount(); //전체 todo 개수 update | |
updateCheckedTodoCount(); //진행중 및 완료 todo 개수 update | |
}); | |
return deleteButton; | |
} | |
function addTodoItem(todo){ | |
const todoItem = document.createElement("div"); | |
todoItem.classList.add("todoWrapper"); | |
const checkbox = createCheckbox(todo.checked); | |
const todoText = createTodoText(todo.text, todo.checked); | |
const deleteButton = createDeleteButton(todo, todoItem); | |
} |
|
||
//입력버튼을 클릭 시 해당 함수가 trigger 되게 addEventListener 등록 | ||
addTodo.addEventListener("click", () => { | ||
const text = todoInput.value.trim(); //입력한 todo의 불필요한 공백 제거 |
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.
trim()으로 공백 예외처리까지 너무 좋습니다
storedTodos.forEach((todo) => { | ||
addTodoItem(todo); | ||
}); |
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.
forEach나 filter같은 고차함수 사용도 잘 하시는 것 같아요
html { | ||
font-size: 10px; /* Default font size */ | ||
|
||
/* Media query for tablet */ | ||
@media (min-width: 768px) and (max-width: 1023px) { | ||
font-size: 8px; | ||
} | ||
|
||
/* Media query for mobile */ | ||
@media (min-width: 450px) and (max-width: 768px) { | ||
font-size: 6px; | ||
} | ||
@media (max-width: 450px) { | ||
font-size: 4px; | ||
} | ||
} |
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.
모바일에서의 사용도 고려해서 구현하신게 인상 깊습니다!!
} | ||
}); | ||
|
||
updateTotalTodoCount(); |
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.
초기에 화면 접속하면 진행 중, 완료까지 뜨도록 updateCheckedTodoCount() 함수도 호출하면 좋을거 같아요!
function updateTotalTodoCount() { | ||
totalTodoCount.textContent = "할 일: " + storedTodos.length; | ||
} | ||
|
||
//현재 체크된 todo를 계산하여 진행중인 todo와 완료된 todo를 구분하는 함수 | ||
function updateCheckedTodoCount() { | ||
const checkboxes = document.querySelectorAll(".todoCheckbox"); | ||
const checkedCount = Array.from(checkboxes).filter( | ||
(checkbox) => checkbox.checked | ||
).length; //만들어진 체크박스들을 배열로 불러온 뒤, 각각의 체크 상태를 판단하여 체크된 개수를 저장 | ||
|
||
nowTodo.textContent = "진행중: " + (storedTodos.length - checkedCount); | ||
doneTodo.textContent = "완료: " + checkedCount; | ||
} |
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.
할일, 완료, 진행중 count를 확인할 수 있어 사용성이 정말 좋은거 같습니다!
checkbox.addEventListener("change", () => { | ||
todo.checked = checkbox.checked; //해당 todo의 체크박스 상태가 변하면 그 상태를 저장해 준 후 | ||
saveTodos(); //localStorage의 체크박스 상태 변화 저장 | ||
todoText.style.textDecoration = checkbox.checked ? "line-through" : "none"; //체크박스가 check 되어있다면 해당 todo에 줄 그어줌 |
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.
삼항연산자로 line 처리를 해줘서 코드를 간결하게 작성하신게 인상 깊습니다!
안녕하세요 1주차 코드 리뷰 파트너 변지혜입니다!😄 |
<title>Vanilla Todo</title> | ||
<link rel="stylesheet" href="style.css" /> | ||
<title>Todo List</title> | ||
<link rel="stylesheet" href="./css/header.css" /> |
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.
css 파일 2개로 나눈 거 유지 보수할 때 용이할 것 같아요! 한 페이지에 모든 css를 담아야겠다고 생각했던 과거의 저를 조금 반성하게 되네요ㅎㅎ
display: flex; | ||
align-items: center; | ||
border-radius: 1rem; | ||
} |
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.
width 뿐만 아니라 padding, border-radius까지! 모두 반응형이 가능한 단위들로 쓰셨네요! 은연중에 px 단위로 적게 되는 모든 단위들을 반응형이 가능한 단위로 생각할 수 있다는 사실과 함께, 이렇게도 쓸 수 있구나 배워갑니다. 또 반응형 뿐만 아니라 설정들을 적용하는 단위를 쪼개는 것에서 전반적인 css 에 대한 이해도가 높으신 게 느껴지는 것 같습니다ㅎㅎ!
</header> | ||
<div class="todoBoardList"></div> | ||
<div class="boards"> | ||
<div class="board" id="toDoBoard"> |
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.
이번 과제에서는 html 구조가 단순한 편이긴 하지만 1주차 Key Questions 에 Semantic tag에 대한 질문이 있었으니, 이를 활용해서 코드를 짜면 장점들을 이용할 수 있어 좋을 것 같아요!
<header> | ||
<div class="todoTitle">TO-DO LIST</div> | ||
<div class="headerBtnWrapper"> | ||
<div class="headerBtn" id="totalTodo"></div> |
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.
class / id 이렇게 주면 굳이 상위 div로 묶어주지 않아도 한 번에 스타일 설정할 수 있어서 좋네요! class와 id 사이에 어떤 기준으로 무엇을 선택해야 하나만 고민했었는데 이렇게 한 번에도 쓸 수 있겠구나 배워갑니다😄
|
||
nowTodo.textContent = "진행중: " + (storedTodos.length - checkedCount); | ||
doneTodo.textContent = "완료: " + checkedCount; | ||
} |
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.
여기서 filter 함수를 이용해 한 번에 체크 상태로 분류하는 거 정말 좋습니다..! 제 코드에도 checked 상태를 관리할 수 있게 수정하면 조금 더 효율적으로 바꿀 수 있겠다 싶어서 와닿는 게 더 크네요ㅎㅎ
const todoText = document.createElement("div"); | ||
todoText.textContent = todo.text; //불러온 todo의 text 값을 생성한 요소에 저장 | ||
todoText.classList.add("todoText"); | ||
todoText.style.textDecoration = todo.checked ? "line-through" : "none"; //불러온 todo의 체크 상태에 따라 줄을 그을지 말지 판단 |
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.
스타일 관련 설정을 js에서 어떻게 잘 할 수 있을까 고민했었는데 이렇게도 할 수 있군요! 삼항연산자까지 사용해 깔끔하게 표현한 점 좋은 것 같습니다.
배포 링크: https://vanilla-todo-18th.vercel.app/
1주차 과제로 투두리스트 만들기를 구현해보았습니다.
인풋에 todo를 입력 후 엔터 혹은 버튼을 누르면 todo가 추가됩니다. 투두가 추가될 때 동적으로 체크박스와 인풋에 입력한 값, 그리고 투두를 삭제할 수 있는 버튼을 만들어서 인풋 아래에 차례대로 뿌려줍니다.
체크박스를 누르면 해당 일을 다 했다는 의미로 줄이 그어지며 x 버튼을 누르면 해당 todo가 삭제됩니다.
전체 todo의 개수를 띄워주며 체크박스가 눌린 todo의 개수는 완료로, 안눌린 todo의 개수는 진행중으로 분류되어 보여줍니다.
해당 todo의 텍스트와 체크의 여부를 묶어 객체로 관리하여 todo가 추가되거나 체크 상태가 변할 때, 혹은 삭제될 때 마다 localStorage 에 해당 사항을 업데이트하여 저장합니다.
localStorage 에 저장했기 때문에 새로고침해도 해당 todo의 상태와 값이 그대로 보여지게 됩니다.
media-query 를 적용 후 rem 단위로 작업하여 모바일부터 pc 까지 문제없이 활용할 수 있도록 작업하였습니다.
사실 그렇게 어렵지 않은 과제라고 생각을 했지만, 오히려 초심으로 돌아가려니까 생각보다 헤맸던 부분이 꽤 있었습니다. 너무 React 에 익숙해진 탓에 class 및 id 이름을 직접 생성하여 css 파일에 적용하고, addEventListener 를 적용하는 등 오랫동안 하지 않았던 방식으로 코딩을 하니 어색함을 느꼈고, 기본기가 좀 부족하다는 것을 느끼고 반성도 하게되는 시간이였습니다.
사실 깔끔하게 기능단위로 커밋을 날렸어야 했는데 까먹고 본의아니게 완성후 커밋을 날렸다.. 앞으로는 기능단위로 커밋을 날려 코드리뷰도 편하게 할 수 있도록 작업하도록 하겠습니다 죄송합니다 ㅠㅠ
우리가 프로젝트를 하면서 코드를 수정하게 되면 이 DOM 구조에 접근하여 수정된 태그에 접근을 하여 업데이트 하게 되는데 수정사항이 있을 때마다 DOM 에 직접 접근하는 것은 매우 비효율적이다. 따라서 React와 Vue 등은 Virtual DOM 을 활용한다. 수정 전의 Virtual DOM 과 수정하려는 Virtual DOM 의 비교과정을 거친 후 변경된 태그들만 DOM에 직접 접근하여 업데이트를 해주는 방식으로 매우 효율적인 방식이라고 알고 있다.
또한 알아본 바로 HTMLElement.insertAdjacentHTML() 라는 함수를 사용하기도 한다. "beforebegin", "afterbegin", "beforeend", "afterend"와 같이 위치를 지정하는 옵션을 활용하여 요소 내에 HTML을 삽입할 때 사용된다.
insertAdjacentHTML 함수는 XSS공격 등 보안에 취약하다는 단점이 있고, createElement 함수가 동적으로 요소를 간단히 생성할 수 있다는 장점이 커서 createElement 함수를 사용하는 것이 적합하다고 판단된다.
또한 검색엔진은 태그를 기반으로 페이지 내 검색 키워드의 우선순위를 판단하므로 Semantic tag를 적극 활용하는 것이 검색엔진을 최적화 시키는 방법 중 하나일 수 있다.
display:flex 로 수평배열 된 자식 요소들을 기본적으로 수직배열되게 하고 싶다면 flex-direction:column 을 활용한다. 이를 활용하게 되면 위와 반대로 justify-content 문법이 자식 요소들의 수직 배치를, align-items 문법은 자식 요소들의 수평 배치를 맡게 된다. 또한 space-between, flex-start, space-around 등 세부적으로도 배치를 조절할 수 있다.
또한 전세계에서 사랑받는 언어로 모르는 부분이 있을 때 구글링을 하면 자료가 매우 방대하여 빠르게 해결할 수 있다는 것도 큰 장점이다. 이처럼 자바스크립트는 매우 유연한 코딩을 도와주고 넓은 생태계 등의 장점을 가지고 있다.
이러한 코드들에 주석을 달아준다면 상대방의 수월한 코드리뷰를 이끌어낼 수 있고, 추후에 리팩토링 및 유지보수를 할 때에도 주석을 읽으며 코드를 보면 훨씬 더 빠른 작업을 할 수 있다고 생각을 하기 때문에 이렇게 주석을 단다면 좋을 것 같다.