-
Notifications
You must be signed in to change notification settings - Fork 11
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.
기본적으로 html DOM 조작을 하는 Javascript의 특성을 너무 잘 이해하고 계신 것 같습니다.
로컬스토리지에 특정 날짜의 할일을 배열로 관리해주시는 것도 인상 깊고, 동일한 내용의 삭제를 위해서 task 별로 index를 들고 있는 것도 좋구요!
다만 현재 비정상적으로 todo의 내용을 길게 작성했을 때 컨텐츠가 그냥 task 요소를 넘어가는 현상이 있어요. 이걸 overflow-wrap
등을 활용해서 수정하시면 더 좋을 것 같습니다. 과제 진행하시느라 고생 많으셨습니다 🙃
font-family: 'SUIT-Regular', sans-serif; | ||
margin: 0; | ||
padding: 0; | ||
box-sizing: border-box; |
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.
전체 요소에 content-box 가 아니라, border-box 속성을 적용해주셔서 border
, padding
을 너비와 높이에 포함해주셨네요! 해당 설정은 예측성을 높일 수 있어 좋은 것 같습니다 👍
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.
정성이 담긴 코드리뷰 감사합니다. 많은 것을 배워가고, 더 공부할 수 있는 걸 발견할 수 있음에 한번 더 감사합니다! overflow-wrap도 적용하였습니다!
@@ -1 +1,261 @@ | |||
/* 본인의 디자인 감각을 최대한 발휘해주세요! */ |
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.
제가 slack 채널에 공유드린 것처럼, reset.css
파일이나 noramlize.css
파일을 설정해두시면 브라우저 밴더마다 약간 다르게 동작하는 부분의 차이를 보완해줄 수 있을 것 같아요! 레퍼런스 같이 남길게요 :)
.container { | ||
width: 360px; | ||
height: 80vh; | ||
margin: 0 auto; |
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.
container 요소 자체에 flex 시스템을 적용하지 않고, margin을 통해 중앙 정렬을 자연스럽게 해주셨네요! 저도 개인적으로 flex가 좋기는 하지만, 너무 과도하게 남발하면 프로젝트의 사이즈가 커질 경우 고려할 것이 많아지는 것 같더라구요:) 자연스러운 활용 좋습니다!
style.css
Outdated
background-color: transparent; | ||
height: 25px; | ||
border: none; | ||
border-bottom: 1px solid #333; |
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.
현재 input 의 하단 실선을 위해 border-bottom
속성을 활용해주셨네요. 이 방법도 너무 좋습니다. 저는 픽셀이 디자이너가 정해준 컨테이너의 너비 높이에 약간 영향을 줄 수 도 있어서, box-shadow
를 활용하고 inset
속성을 통해 예상치 못한 작용을 방지하기도 해욥!
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.
input 하단 실선을 [box-shadow](box-shadow: inset 0px -1px 0px 0px rgba(0, 0, 0, 1);) 로 교체했습니다. 좋은 방법 알려주셔서 감사합니다!
nav ul li.selected { | ||
border-radius: 0; | ||
color: #89bfeb; | ||
border-bottom: 2px solid #89bfeb; | ||
span { | ||
color: #89bfeb; | ||
} | ||
} |
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.
사용자가 해당 날짜를 선택했을 때 javascript를 이용해서 속성을 추가하는 부분을 매우 잘 구분해주신 것 같아요! 이를 위해 css 파일이더라도, "추후에 선택된 item에 적용되는 스타일"
이라는 주석을 남겨줘도 나중에 다른 프로그래머가 봤을 때 좋을 것 같습니다
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.
수정완료했습니다!
|
||
// Add Task -> LocalStorage에 추가 | ||
function addTaskToLocal(dateKey, task) { | ||
console.log(dateKey); |
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.
불필요한 콘솔 출력문은 삭제해주세요 :)
script.js
Outdated
const taskTitleInput = document.getElementById('task-title'); | ||
const taskDescInput = document.getElementById('task-desc'); | ||
const taskTitle = taskTitleInput.value; | ||
const taskDesc = taskDescInput.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.
const taskTitleInput = document.getElementById('task-title'); | |
const taskDescInput = document.getElementById('task-desc'); | |
const taskTitle = taskTitleInput.value; | |
const taskDesc = taskDescInput.value; | |
const taskTitle = document.getElementById('task-title').value ; | |
const taskDesc = document.getElementById('task-desc').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.
좋은 의견 감사합니다! 다만 알려주신 코드로 진행했을 때 제 코드 밑에 input field 를 ''로 초기화하는 부분이 있는데 const로 taskTitle을 DOM객체가 아니라 value값 즉, 변수를 선언했기 때문에 밑에서 ''로 초기화할 때 const로 선언된 변수를 새로 선언하는 에러가 발생했어서 DOM 객체와 value를 분리시켜 적용했었습니다! 혹시 제 코드 방법이 아니라 다른 방법으로 input field를 초기화할 수 있는 방안이 있다면 심심하실 때 알려주십셔!
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.
추가로 가지신 의문점에 대해 저도 생각 하나 남겨봅니다!
위에서 말씀해주신대로 코드를 간결하게 줄인다면 아래쪽 초기화 부분은
document.getElementById('task-title').value = '';
document.getElementById('task-desc').value = '';
와 같이 DOM 객체에 직접 접근하는 식으로 바로 쓸 수 있을 것 같습니다! 다만 이렇게 되면 반복적으로 DOM을 탐색하게 되어 성능 면에서는 기존대로 한 번 DOM 접근을 통해 저장하는 방식이 규모가 커진 경우에는 좀 더 유리하지 않을까 싶습니다. 물론 여기서는 코드의 가독성을 위해 줄여도 좋겠다고 생각합니다! 초기화 부분에 대해 또 다른 방안이 있다면 저도 무척 궁금하네요 !!
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.
DOM객체에 직접 접근하는 방식도 좋은 것 같습니다! 다른 방법을 찾아보았는데 이번처럼 title, desc 모든 form의 입력필드를 초기화할 때 taskAddForm.reset();
이 코드처럼 form 내의 모든 요소를 초기화하는 방법도 있는 것 같습니다. 다만 말 그대로 모든 요소를 초기화하는 것이기 때문에 이번처럼 조건이 맞을 때만 사용해야 할 것 같아요!
script.js
Outdated
const taskTitle = taskTitleInput.value; | ||
const taskDesc = taskDescInput.value; | ||
|
||
if (selectedDate) { // 날짜가 선택된 경우에만 저장 |
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.
일단 DOM이 로드 되고 selectedDate는 날짜가 바뀔 때에도 웬만하면 존재는 해보이는데 따로 조건문으로 빼주신 이유가 있을까요?
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.
이 부분은 이전 selectedDate를 let selectedDate = '';로 선언했을 때를 고려하여 조건문으로 설정했었는데 코드 수정된 후에 변경이 안 되었는 것 같습니다. 지적 감사합니다!
<button class="task-delete" data-index="${index}">delete</button> | ||
</div> | ||
`; | ||
taskElement.querySelector('.task-delete').addEventListener('click', function () { |
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.
innerHTML에 요소를 넣어주시고, 그 다음 이벤트 핸들러를 달아주신 것 좋습니다.
제가 QA를 위해 동일한 내용의 todo를 배열에 중복으로 넣고 삭제를 했을 때 알맞은 것이 삭제되나 확인해봤는데, index 값을 처음 DOM에 추가될 때 잘 가지고 있어 제대로 동작하는군요!
이 부분까지 고려하시는 것 너무 좋았던거 같아요. React는 이런 것을 자체적으로 해주므로 너무 편한 기술인 것을 알 수 있는 것 같아요 💪🏼
script.js
Outdated
modalBtn.addEventListener('click', () => { | ||
modalBtn.style.display = 'none'; | ||
taskAddForm.style.display = 'flex'; | ||
}); | ||
|
||
// back navigation | ||
backBtn.addEventListener('click', () => { | ||
modalBtn.style.display = 'block'; | ||
taskAddForm.style.display = 'none'; | ||
}); |
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.
리액트의 조건부 렌더링이 그리워지는 순간이네요...! button 컴포넌트는 기본적으로 inline-block
으로 동작해서 여기에서는 크게..? block으로 갈 필요는 없을 것 같아요! 어차피 지금 footer 컴포넌트가 flex이고 버튼이 자체적으로 50, 50의 너비와 높이를 가지니까요!
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.
구현 난이도가 높은 달력 기능까지도 정말 잘 구현하신 것 같아요🥹👍 코드 리뷰를 하려면 코드를 먼저 이해하는 게 필수라는 걸 느끼며 뜯어볼수록 어려우셨겠다는 생각이 많이 들었습니다. 주석도 꼼꼼히 달아주셔서 코드 따라가기 용이했어요! 1주차 과제 고생 많으셨습니다!!
script.js
Outdated
// 날짜 계산 | ||
const firstDayOfWeek = new Date(selectedDateObj); | ||
firstDayOfWeek.setDate(selectedDateObj.getDate() - selectedDateObj.getDay() + 1); // 월요일(0: 일요일, 1: 월요일) |
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.
배포링크에서 확인해보니 현재 날짜가 8일(일요일)인데 주차는 9일(월요일)부터 시작인 주차가 뜨는 현상이 있었습니다! firstDayOfWeek
을 정의한 곳에서 getDay()
값이 0인 일요일에 대해서도 +1되면서 그 다음주차가 뜨는 로직때문인 것 같습니다.
selectedDateObj.getDay()
를 변수로 저장하여 값이 0일 때(일요일인 때)와 그렇지 않을 때를 조건문으로 구분해서 예외적으로 처리해주면 정확한 주차가 뜰 것 같네요😀
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.
미처 확인하지 못한 부분인 것 같습니다. 짚어주셔서 감사합니다!
script.js
Outdated
const taskTitle = taskTitleInput.value; | ||
const taskDesc = taskDescInput.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.
입력값에 공백문자만 들어갔을 때도 빈칸으로 제출되기 때문에 trim()
함수를 적용한 입력값의 존재여부를 판단함으로써 이를 막는 처리를 해주면 더 좋을 것 같습니다! 참고링크 남겨드립니다
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.
세심한 부분까지 알려주셔서 감사합니다. 덕분에 앞으로 예외사항을 더 고려할 수 있을 것 같습니다.
<!-- main --> | ||
<main class="task-list"></main> | ||
<!-- footer --> | ||
<footer> |
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.
주석으로 시맨틱 태그를 구분해주셔서 구조가 한 눈에 들어오니 읽기에도 편하네요👍 사소한 습관이지만 좋은 것 같습니다!
<button type="submit" class="btn save">save</button> | ||
<button type="button" class="btn back">back</button> |
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.
form 태그 내의 button을 저는 기본 타입인 submit으로만 사용했어서 간과하고 있었는데, 덕분에 type에 관해 알아보게 되어서 적극 활용해야겠다는 생각이 듭니다 😂
script.js
Outdated
dayElement.addEventListener('click', () => { | ||
selectDate(dayElement); | ||
selectedDate = dateKey; | ||
datePicker.value = dateKey; | ||
loadTasks(dateKey); | ||
}); |
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.
dayElement라는 li 요소에 리스너가 추가되어있는데, 이번에 배운 버블링 개념을 적용해보면 좋을 것 같아요. 상위 요소인 ul (weekDays)에 이벤트를 위임하면 li 개수만큼 리스너를 여러 번 할당할 필요 없이 한 번만 추가하여 쓸 수 있으니까요! 참고하면 좋을 만한 링크 달아두겠습니다
https://myeongsu0257.tistory.com/76
https://gobae.tistory.com/143
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.
이렇게 레퍼런스 남기는 리뷰 너무 좋습니다
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.
오 버블링 개념을 바로 활용하시는 부분이 정말 뛰어난 것 같습니다. ul에만 리스너를 한번 할당하게 된다면 메모리, 유지보수, 조작비용 측면에서 많은 이점이 있음을 알게 되었습니다. 지원님 코드에서의 closest을 활용해서 수정하는 방안으로 해보겠습니다. 감사합니다.
.task-delete { | ||
position: absolute; | ||
bottom: 10px; | ||
right: 5px; | ||
background-color: white; | ||
color: black; | ||
border-radius: 8px; | ||
padding: 2px; | ||
border: none; | ||
cursor: pointer; | ||
} | ||
.task-delete:hover { | ||
background-color: lightgray; | ||
} | ||
} | ||
} |
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에서 시맨틱 태그와 태그들을 전반적으로 잘 활용하신 것 같아요! 저는 시맨틱 태그를 정작 css에서는 구조화시키지 않고 다 별개로 썼어서 배워갑니다😂 호버 시 효과까지 주는 디테일도 넘 좋아요👍
input[type="date"] { | ||
-webkit-appearance: none; | ||
-moz-appearance: textfield; |
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에서 input 타입을 이렇게 지정해서 특정 입력 유형에 대해 스타일을 적용할 수 있다는 것도 알아갑니다!
const tasks = JSON.parse(localStorage.getItem(dateKey)) || []; | ||
tasks.splice(taskIndex, 1); | ||
localStorage.setItem(dateKey, JSON.stringify(tasks)); | ||
loadTasks(dateKey); |
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.
tasks에 할당된 JSON.parse(localStorage.getItem(dateKey)) || []
가 아래쪽의 loadTasks
함수 내에서도 반복되고 있어서 별도의 함수로 만들어 재사용하면 더 용이할 것 같다는 생각이 듭니다!
style.css
Outdated
main article { | ||
width: 100%; | ||
height: auto; | ||
padding: 0 10px; | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
background-color: #f8f8ff; | ||
border-radius: 10px; | ||
|
||
.task-detail { | ||
position: relative; | ||
width: 100%; | ||
padding: 20px; | ||
display: flex; | ||
flex-direction: column; | ||
gap: 5px; | ||
|
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.
특히 이 부분에서만 네스팅 구조로 작성하신 이유가 궁금합니다!
음.. position
에서 relative
와 absolute
관계가 잘 드러날 수 있기 때문일까요??
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.
네스팅 구조를 사용한 목적은 article 안에서의 css 스타일을 조금 더 가독성있고, 명시적으로 보기 위함이었습니다.
다만 네스팅 구조를 사용했을 때 따라오는 단점을 고려하지 않고, 단순한 가독 목적으로 사용했던 판단이 너무 가벼웠던 것 같습니다. 이에 대해 공부하면서 네스팅 구조가 가져오는 렌더링 문제, css 우선순위로 인한 문제 등을 인지할 수 있었습니다. 관련해서 SCSS CSS 전처리기도 조금 엮여있는 것 같아 한번 링크 남겨보겠습니다. SCSS 공부도 필요하다는 생각도 들어서 희원님 질문이 많은 것을 알려주신 것 같아서 감사드리고 싶습니다!!
script.js
Outdated
const newTask = { | ||
title: taskTitle, | ||
desc: taskDesc, | ||
time: new Date().toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', second: 'numeric' }) | ||
}; |
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.
제 코드를 보고 스스로 개선하면 좋겠다고 생각한 부분이 있었는데, 영준님 코드에도 비슷한 부분이 있어 공유드리고자 리뷰합니다.
새로운 테스크를 만들 때, 생성자를 활용하는 방법도 좋을 것 같습니다.
참고자료
const month = ('0' + (date.getMonth() + 1)).slice(-2); | ||
const day = ('0' + date.getDate()).slice(-2); |
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.
지나가다 한줄 남깁니다..!🙇♀️🙇♀️ padStart()
같은 메소드를 이용하면 문자열 앞을 원하는 길이가 될 때까지 패딩을 넣어 줄 수 있습니당!!
배포 링크 : https://vanila-todo-1th.vercel.app/
느끼고 배운 점 : 이번에 html, javascript, css로 코드를 짜보면서 보통 React나 Next.js로 코딩할 때 html, css는 사용하더라도 javascript는 사용하지 않기 때문에 간만에 js를 사용하는 거라 반가우면서 걱정했었습니다. 물론 여러 도구를 이용해 구현에는 어려움이 없지만, Key Question과 직접 JavaScript코드를 짜보면서 DOM에 대한 것과 React와 같은 라이브러리를 왜 사용하는지, 어떤 장단점이 각각 있는지 알 수 있었습니다. 지금 코드에는 DOMContentLoaded 이벤트 리스너 안에 함수를 몰아넣어서 구현하였는데 이렇게 했을 때의 장단점과 더 효율적인 방법이 혹은 수정할 수 있는 부분이 있는지 미션 제출 후 공부하려 합니다. 만약 코드리뷰 하시면서 개선할 부분이나 방법이 있다면 가감없이 막 말씀해주시면 감사하겠습니다. 가능한 주석을 달았지만, javascript에는 미숙하여 가독성을 미처 신경쓰지 못했기에 더 간소화하고, 효율적인 javascript구현이 어려웠던 것 같습니다. 공부해야 할 부분이 아직 많아서 즐겁고, 이번 기회를 통해 React 라이브러리 소스 코드에 관심이 생겨 JavaScript와 연결지어 더 알아보려 합니다.
[Key Questions]
이벤트가 발생했을 때 이벤트가 DOM 트리를 통해 전파되는 방식.
캡처링 단계(=트리클링) - 이벤트가 최상위 요소(루트)부터 이벤트가 발생한 요소(타겟)까지 내려가는 과정
이벤트 흐름 제어를 캡처링으로 하는 목적 -> 상위 요소에서 이벤트를 미리 처리하고, 하위 요소로 이벤트를 전달하기 이전에 이벤트 필터링 및 선처리 목적 - ex) 하위 요소에서 발생한 이벤트를 상위 요소 이벤트 리스너에서 특정 조건을 달성했을 때만 하위 요소로 이벤트를 전달함
장점 -> 이벤트 흐름제어, 선처리, 로깅분석 / 단점 -> 복잡성, 이벤트 흐름에 대한 완전한 이해가 없을 시 예측 불가
버블링 단계 - 이벤트가 발생한 타겟 요소에서 시작하여 최상위 요소까지 거슬러 올라가는 과정. 이벤트 리스너의 기본동작값은 버블링 방식임.
이벤트 흐름 제어를 위한 메서드 stopPropagation & preventDefault
stopPropagation -> 이 메서드를 사용한 이벤트 리스너에서만 이벤트 흐름이 이루어지고, 이외에 현재 이벤트가 다른 요소로 전파되지 않도록 막음.
preventDefault -> 링크 클릭 시 페이지 이동, 폼 제출 시 페이지 새로고침 등의 기본동작을 막아 JavaScript로 커스터마이즈된 동작이 수행가능케 함.
클로저(Closure) -> 함수와 함수가 선언된 환경을 기억하는 것. (정적 스코프에 해당하는 렉시컬 스코프(Lexical scope): 함수가 선언될 때 스코프가 정해진다는 의미로 선언된 위치에 따라 스코프가 결정됨.)렉시컬 스코프 덕분에 함수가 선언될 때의 외부 스코프에 있는 변수와 같은 환경들을 기억하여 이후 활용 시 참조가 가능함.
사용목적 -> 상태 유지, 함수 외부에서 변수를 은닉하고, 특정 함수를 통한 접근이 가능한 캡슐화 형태 구현 용이, 비동기 처리, 콜백함수에서 변수 값을 안전하게 유지 가능
스코프(Scpoe) -> 변수, 함수, 객체 등의 유효한 범위
전역 스코프(Global Scope) -> 코드 어디에서나 접근 가능. 전역 객체
지역 스코프(Local Scope) -> 함수 내에서 정의된 변수. 함수가 호출될 때에만 접근 가능. 함수 외부 접근불가
블록 스코프(Block Scope) -> let, const 로 정의된 변수에 적용. {} 블록 내에서만 유효함. -> 블록 스코프는 독립적이며 블록 스코프 내의 변수는 다른 블록 스코프나 다른 스코프에서 접근이 안 됨. -> ex) for문에서 setTimeout을 이용한 비동기 예시가 있을 때 (let i) 변수는 매 반복마다 독립된 블록 스코프로 감싸지기 때문에 비동기로 for함수가 실행된다 하더라도 블록 스코프내의 변수값은 유지됨.