Conversation
Wannys26
left a comment
There was a problem hiding this comment.
과제하시느라 고생많으셨습니당!!
다음 과제가 react + tailwind 마이그레이션이기에,
html + css + js 로 구현했을때보다
react와 tailwind가 어떤 점이 더 나은지? 어떤 점이 달라지는지? 를 고민해보셨으면 하는 마음으로 리뷰 달았습니다!
노력이 깃든 코드 볼 수 있어서 좋았습니당 ㅎㅎ
There was a problem hiding this comment.
:root {
/* color */
--color-black: #3f454d;
--color-white: #ffffff;
--color-yellow: #fbbe28;
}
.item {
background-color: var(--color-white);
}
:root 를 활용하셔서, 색상, 폰트, shadow 등등 여러가지 값들을 변수 선언(디자인 토큰으로 정의)하시는 걸 추천드립니당!
디자인과 협업을 한다고 생각했을때, 만약
"background-color: #f5f5f7; 해당 색상을 좀 더 밝게 바꿔도 괜찮을까요?" 라고 한다면
:root 에서 정의해둔 값의 hex 코드만 바꾼다면 쉽게 해결이 될 수 있으니까요!
+) 유민님 css 파일 참고하시면 좋을 듯합니당!
There was a problem hiding this comment.
디자이너의 디자인 토큰 정의
결국 디자이너도 이미 변수값으로 정의해둔 (색상 + 폰트 + ..etc) 등을 조합해서 피그마 디자인을 만들어내기 때문에,
디자이너의 수정 사항 및 여러 변수에 대응하기가 더 쉬워집니당
| font-size: 14px; | ||
| } | ||
|
|
||
| /* Dark Mode */ |
There was a problem hiding this comment.
dark모드 관련 코멘트도 위의 :root 정의랑 이어지는 이야기입니다!
만약 구현하셔야 되는 내용이 하나의 페이지가 아니라,
여러개의 페이지에 전부 dark모드가 일괄 적용 되어야 한다면,
변수로 지정해두시는게 dark 모드 대응이 수월할 것입니당!
다크 모드라면 기본적으로 검은색 -> 흰색 변환 이런식일텐데,
라이트 모드라면? -> 검은색 색상 변수를 사용
다크 모드라면? -> 흰색 색상 변수를 사용
요런식으로 일괄적으로 적용될 수 있을 것입니당~
| return; | ||
| } | ||
|
|
||
| currentTodos.forEach((todo, index) => { |
There was a problem hiding this comment.
forEach의 index를 todo 식별자로 사용하고 있는데,
지금은 render()에서 매번 리스트를 다시 그려서 괜찮지만,
다음 과제에서 react로 구현시 문제가 될 수 있습니다!
react는 변경된 요소만 재렌더링하기 때문에, 고유한 id를 주지 않는다면
중간 항목을 삭제하면 index가 밀리면서 어떤 항목이 삭제된 건지 올바르게 판단하지 못할 수도 있습니당
각 todo에 고유 id(crypto.randomUUID() (꼭 uuid를 무조건 쓰라는건 아닙니다 "고유한"에 좀 더 중점을 맞추시는게 좋음))
를 부여해주시는게 좋아용
| /* ── Render All ── */ | ||
| function render() { | ||
| currentDateEl.textContent = formatDisplayDate(selectedDate); | ||
| renderWeek(); | ||
| renderTodos(); | ||
| } |
There was a problem hiding this comment.
다음 과제가 react 마이그레이션인 만큼,
render All 하는 부분에 대해서 react는 어떤 차이점을 가지지?
라는 점에 대해 생각해보시면 좋을거 같습니당
| if (savedTheme === "dark") { | ||
| document.body.classList.add("dark"); | ||
| themeToggleBtn.textContent = "\u2600\uFE0F"; | ||
| } |
There was a problem hiding this comment.
다크모드 상태를 localStorage에 저장해서 새로고침 후에도 유지되도록 구현한 점이 좋은 것 같습니다!
추가로 html 파일에서 버튼의 aria-label은 고정값인데, 다크/라이트 상태에 따라 "다크모드 켜기" / "라이트모드 켜기" 처럼 함께 바뀌면 접근성 면에서 더 좋을 것 같아요!
| class="todo-form__input" | ||
| id="todoInput" | ||
| placeholder="할 일을 입력하세요" | ||
| required |
There was a problem hiding this comment.
required를 넣어주셔서 빈 입력을 제출할 수 없도록 처리한 점이 좋았습니다!
별도의 복잡한 로직 없이도 기본적인 입력을 검증할 수 있다는 점이 좋은 것 같아요 :)
| } catch { | ||
| return {}; | ||
| } | ||
| } |
There was a problem hiding this comment.
에러 상황을 대비해 try...catch로 예외 처리를 해두신 점이 좋았습니다!
localStorage 데이터가 깨져 있거나 예상과 다른 값일 때 앱이 바로 죽지 않도록 방어해둔 점이 안정적이라고 생각했어요. 덕분에 localStorage를 다룰 때 이런 식으로 방어적으로 작성할 수 있다는 걸 배울 수 있었습니다!
There was a problem hiding this comment.
주석으로 코드에 대한 설명을 자주 자세하게 작성해주셔서 코드를 전반적으로 이해하기 편했습니다! 특히 협업할 때 가지면 좋은 습관인 것 같습니다.
| li.appendChild(dayNumber); | ||
| li.appendChild(dayCount); | ||
|
|
||
| li.addEventListener("click", () => { |
There was a problem hiding this comment.
렌더링될 때마다 같은 요소에 이벤트를 새로 바인딩하면 성능 문제가 발생할 수 있습니다. weekDaysEl 등에 이벤트 위임하는 식으로 처리하면 성능 문제를 관리할 수 있을 것 같아요!
하단의 checkbox, deletebtn 등에 대해서도 todoList에 이벤트 위임을 해서 처리하면 더 좋을 것 같습니다.
배운 점
1. DOM 조작
document.createElement,appendChild를 활용해 HTML 요소 동적으로 생성하고 DOM 트리에 추가할 수 있다.2. JSON 직렬화/역직렬화와 객체 기반 데이터 관리
localStorage에는 문자열만 저장할 수 있기 때문에,JSON.stringify()와JSON.parse()를 활용해 객체 형태의 데이터를 저장하고 다시 복원하는 방식으로 사용할 수 있다.3. Semantic HTML
div처럼 단순한 구조용 태그와 달리, 태그 자체가 해당 영역의 의미와 역할을 나타내는 HTML 요소를 의미한다.main,header,nav,section,form등의 시맨틱 태그를 활용해 페이지의 구조를 의미 단위로 명확하게 구분할 수 있다.4. CSS Flexbox
display: flex,flex-direction,gap,flex: 1등의 속성을 활용해 요소들을 유연하게 정렬하고 간격을 관리할 수 있다.5. BEM 네이밍
block__element--modifier구조를 사용하여 컴포넌트 단위(Block), 그 내부 요소(Element), 상태(Modifier)를 구분하여 표현할 수 있다.많은 시간을 투자한 부분
주간 네비게이션 날짜 계산
Date객체에서 제공하는getDay(),getDate(),setDate()등의 메서드를 활용해 현재 날짜를 기준으로 해당 주의 시작 날짜(일요일)를 계산하는 로직을 구현했습니다.getDay()는 요일을 숫자 인덱스로 반환하며, 일요일이0, 월요일이1로 시작한다는 점을 고려해 주간 날짜 계산 로직에 반영했습니다.배포 링크
🔗 https://ceos-week1-vanilla-todo-23rd-mk2a.vercel.app/
Review Questions
1. DOM은 무엇인가요?
<script>태그를 만나면 DOM 생성을 일시 중단하고 자바스크립트를 먼저 실행한다.<script>위치에 따라 DOM 생성이 지연될 수 있으므로, DOM이 먼저 생성될 수 있도록 스크립트는 문서 하단에 배치하는 것이 권장된다.2. 이벤트 흐름 제어(버블링 & 캡처링)이 무엇인가요?
캡처링: 이벤트가 DOM 트리의 최상위 요소(root)에서 시작해 실제 이벤트가 발생한 요소(target)까지 내려가는 과정
-
Window → Document → html → body → ... → Target Element- 이 과정에서 상위 요소들이 타겟 요소보다 먼저 이벤트를 감지할 수 있다.
버블링: 실제 이벤트가 발생한 요소에서 시작해 DOM 트리를 따라 상위 요소로 올라가는 과정
-
Target Element → Parent Element → ... → Document → Window- 대부분의 DOM 이벤트는 버블링 단계에서 처리된다.
이벤트 흐름을 어떻게 활용 가능할까?
3. 클로저와 스코프가 무엇인가요?
스코프: 현재 어떤 값이나 표현식에 접근할 수 있는 실행 문맥
- 어떤 변수나 함수가 어디서부터 어디까지 유효한지, 어디에서 참조할 수 있는지를 결정하는 규칙
- 스코프는 계층 구조를 가지며, 안쪽 스코프는 바깥쪽 스코프에 접근 가능하지만, 그 반대는 불가능하다.
- 전역(Global) 스코프, 지역(Local/Function) 스코프, 블록(Block) 스코프로 나뉜다.
-
var는 함수 레벨,let/const는 블록 레벨을 따른다.클로저: 주변 상태에 대한 참조와 함께 묶인 함수의 조합
- 함수가 선언될 당시의 외부 스코프를 기억하고, 이후에도 그 스코프 내의 변수들에 계속 접근할 수 있다.
- 함수가 자신의 외부 어휘적 환경을 기억하기 때문에 가능하다.
예시