[4팀 오하늘] Chapter 1-2. 프레임워크 없이 SPA 만들기 #60
Conversation
- 인자 vNode가 null, undefined, boolean 타입일 때 빈값 리턴 - 인자 vNode가 string, number 타입이면 문자열로 리턴 - 인자 vNode가 function일 때 재귀로 펑션 리턴 - 인자 vNode가 함수형 컴포넌트일 때는 작업 중
- vNode가 null, undefined, boolean일 때 빈 문자열 리턴 - string, number 타입은 문자열로 변환하여 리턴 - 함수형 컴포넌트 처리 로직 추가 및 자식 요소 정리 기능 구현
- vNode가 null, undefined, boolean일 때 빈 텍스트 노드 리턴 - string, number 타입은 문자열로 변환하여 텍스트 노드 리턴 - vNode가 배열일 경우 DocumentFragment 생성 및 자식 요소 처리 추가 (진행 중) - vNode가 객체일 때 type 속성을 기반으로 엘리먼트 생성 로직 추가 (진행 중)
- vNode가 배열일 경우 DocumentFragment 생성 및 자식 요소 처리 로직 추가 - vNode가 객체일 때 type 속성을 기반으로 엘리먼트 생성 및 속성 업데이트 로직 구현 - updateAttributes 함수 추가하여 props 처리 기능 구현 - 오류 처리 추가: vNode 처리 불가 시 에러 발생
- vNode가 객체일 때 type 속성이 함수인 경우 에러 발생 로직 추가 - updateAttributes 함수에 id 속성 처리 및 boolean 값 처리 로직 추가
- eventManager.js에 이벤트 위임 및 핸들러 추가/제거 기능 구현 - createElement.js에서 함수형 컴포넌트 처리 로직 추가 - basic.test.jsx에서 컴포넌트 정규화 관련 주석 추가
- renderElement 함수에서 최초 렌더링 및 업데이트 로직 명확화 - DOM 생성 및 이벤트 등록 과정에 대한 주석 추가 - normalizeVNode 호출 및 container._vnode 관리 로직 추가
- childNodes가 비어 있는 형태로 오는 (실제 DOM에 아무것도 없음) 경우 수정 중..
- basic.test.jsx에서 불필요한 console.log 주석 처리 - createElement.js에서 children 처리 로직 수정 및 주석 추가 - normalizeVNode.js에서 children 정리 로직 개선 - renderElement.js에서 DOM 업데이트 로직 주석 처리 - updateElement.js에서 children 접근 방식을 수정
- basic.test.jsx에서 불필요한 console.log 제거 - eventManager.js에서 이벤트 핸들러 등록 방식을 개선하여 중복 제거 - normalizeVNode.js에서 자식 노드 정리 로직 개선 - renderElement.js에서 DOM 업데이트 로직 간소화 및 주석 정리 - updateElement.js에서 속성 업데이트 로직 개선 및 불필요한 코드 제거
- eventManager.js에서 이벤트 핸들러를 Map으로 관리하여 중복 등록 방지 및 기존 핸들러 제거 기능 추가 - updateElement.js에서 속성 업데이트 로직 개선 및 불필요한 코드 제거, className 및 style 처리 방식 수정
- createElement.js에서 boolean 속성 처리 로직을 추가하여 속성 업데이트 시 boolean 값을 올바르게 설정하도록 개선 - renderElement.js에서 oldVNode 변수를 const로 변경하고 updateElement 호출 순서를 수정하여 코드 가독성 향상 - updateElement.js에서 boolean 속성 처리 로직을 추가하고 불필요한 console.log 제거
- createElement.js와 updateElement.js에서 boolean 속성 처리 방식을 수정하여 일관성을 유지 - updateAttributes 함수에서 className 및 일반 속성 처리 방식을 개선하고 불필요한 console.log 제거 - 주석을 추가하여 코드 가독성을 향상
- updateElement.js에서 속성 설정 시 함수 타입 체크 및 null 값 처리 개선 - className 및 boolean 속성 처리 로직 수정 - 중복 속성 설정 제거 및 안전성 체크 추가로 코드 안정성 향상 - 잉여 노드 제거 로직 추가로 DOM 관리 개선
2Estella
left a comment
There was a problem hiding this comment.
하늘 공주님~ 2주차 과제 너무너무 고생하셨어요! ❤️💗💖💓💝
코드에서 고민하신 흔적과 풀어나간 것들이 보여서 코드 구경 잘 하고 가요~!
다음주차도 화이팅 😘🫶
There was a problem hiding this comment.
전반적으로 이벤트 위임의 핵심을 잘 이해하여 설계하고 고민을 많이 하신 코드 같아요! 특히 역할에 따라 eventStore와 eventHandlers를 분리하고 Map/Set을 활용해 데이터 구조를 깔끔하게 설계한 점이 인상적이에요!
제가 처음에 과제 이야기를 하면서 WeakMap을 사용했었단 얘기를 했었는데 이게 오히려 하늘님을 고민을 하게 만들었군요 ㅠㅠ (제 개인적으로는 깨달음을 얻게 된? 그런거라 한번 보셨으면 했어요 ㅎㅎ)
적용 방법 예시는 아래 코드에 남길게요!
| export function addEvent(element, eventType, handler) { | ||
| if (!eventStore.has(eventType)) { | ||
| // 스토어에 이벤트타입이 없으면 set | ||
| // 내부 맵은 어떤 element에 어떤 handler가 등록되어 있는지를 저장 | ||
| eventStore.set(eventType, new Map()); | ||
| } | ||
|
|
||
| // eventStore에서 이벤트 타입에 맞는 값을 뽑아온 후 | ||
| const elementMap = eventStore.get(eventType); | ||
|
|
||
| // 뽑아오려고 했는데 없으면 set | ||
| if (!elementMap.has(element)) { | ||
| // 핸들러의 중복 방지를 위해 set | ||
| // console.log(elementMap, "elementMap.."); | ||
| elementMap.set(element, new Set()); | ||
| } | ||
|
|
||
| elementMap.get(element).add(handler); | ||
| } |
There was a problem hiding this comment.
저는 이 부분에서 WeakMap을 사용했는데요!
Map은 DOM에서 제거된 요소에 대한 강한 참조를 유지해 메모리 누수가 발생할 수 있다고 해요.
그래서 WeakMap을 사용하여 요소가 DOM에서 사라질 때 자동으로 GC 대상이 되어 메모리 관리가 더욱 안전해질 수 있도록 구현했었어요.
예를 들면 아래와 같이 변경할 수 있어요.
export function addEvent(el, type, handler) {
let elementMap = eventStore.get(type);
if (!elementMap) {
elementMap = new WeakMap();
eventStore.set(type, elementMap);
}
let handlers = elementMap.get(el);
if (!handlers) {
handlers = new Set();
elementMap.set(el, handlers);
}
handlers.add(handler);
}| if (typeof vNode === "object" && typeof vNode.type === "function") { | ||
| throw new Error("컴포넌트를 createElement로 처리할 수 없습니다"); | ||
| } | ||
|
|
||
| throw new Error("컴포넌트를 createElement로 처리할 수 없습니다"); | ||
| } |
There was a problem hiding this comment.
함수형 컴포넌트 감지 후 throw와 마지막 throw 메시지가 동일한데, 혹시 두 곳 모두 필요한 이유가 있을까요?
| if (key === "style") { | ||
| Object.assign($el.style, value); | ||
| } |
| if (typeof vNode.type === "function") { | ||
| const props = { | ||
| ...vNode.props, | ||
| children: cleanChildren(vNode.children), | ||
| }; | ||
|
|
||
| const normalizedChildren = cleanChildren(vNode.children); | ||
| const result = normalizeVNode(vNode.type({ ...props, children: normalizedChildren })); | ||
| return result; | ||
| } |
There was a problem hiding this comment.
여기 cleanChildren(vNode.children)를 두 번 호출하고 계신데 혹시 두 번 분리해서 호출하신 특별한 이유가 있을까요?
아니면 한 번만 호출해서 const normalizedChildren = cleanChildren(vNode.children)처럼 재사용하는 편이 더 나을 것 같아요!
There was a problem hiding this comment.
전체적으로 각 함수가 책임 분리 원칙에 따라 깔끔하게 구성되어 있어 가독성이 좋아요!
diff 기반 업데이트 로직도 효과적으로 구현하신 것 같습니다.
다만 PR 올리시기 전에 console.log는 제거 하시는 게 좋을 것 같아요 ㅎㅎ(넘 많아잉)
There was a problem hiding this comment.
공주님들은 weakmap으로 구현했는지 묻고 싶습니다
이 부분에 대해서 나는 한 번도 배워 본 적 없는(다음 학기에 들을..) 자료구조가 나와서
많이 당황슨 당황슨 했는데.. 공주님들은 처음에 이것 보자마자 와! 이 구조다! 하면서 바바박. 작성하셨는지용
There was a problem hiding this comment.
저는 weakMap으로 구현했어요!
송이님이 weakMap이라는 자료 구조가 유용할 거다. 어디 쓰일지 생각해봐라라고 힌트를 주신 적이 있어요.
그게 아니었으면 생각 못했을 것 같긴 한데,
저 말을 듣고 weakMap에 대해서 미리 공부했던 점이 해당 자료구조를 떠올릴 수 있게 해준 것 같아요.
eventHandler의 경우 remove 하는 것이 중요한 일이다 보니,
removeEvent를 만들 때 이걸 어떻게 하면 잘 remove할 수 있을까 라는 고민을 했거든요!
그 때 아 여기서 weakMap을 쓰면 되겠구나 라는 생각을 했습니다!
There was a problem hiding this comment.
저도 weakMap으로 구현했긴 한데요 사실 이런 자료구조가 있는줄도 몰랐지만...
송이님께서 힌트를 주셔서 쓰게 됐슴다!
Map 형태의 자료구조가 필요할것 같은데... > 어라 weakMap이 어디선가 쓰인다고 말씀하셨는데 이 타이밍인가?
요런 사고의 흐름으로 사용했슴다!
There was a problem hiding this comment.
저두 이벤트 매니절에는 그냥 냅다 map으로 썼는데용
updateElement 구현할 때쯤 공주들의 weakmap 언급이 떠올라서 renderElement에서 써먹어봤어요!
컨테이너맵으로 이전에 렌더링 했던 vNode와 해당 vNode로부터 생성된 루트 요소를 가지고 있다가 create인지 update인지 판단하는데, 일반 map으로 구현하면 dom이 변경되고 컨테이너가 교체될 때마다 더이상 필요없는 dom 객체 정보도 가지고 있게 되니까 메모리 누수가 발생할 것 같아서 넣어봤듭니다.
이번에 찐하게 알게 되었으니 map 쓸 때마다 한번씩 떠올려볼 거 같아요!
There was a problem hiding this comment.
저는 eventManager에서 ai의 제안으로 Map으로 구현했습니다. Map은 JavaScript에서 자주 사용하는 자료구조라서 이벤트 핸들러를 저장하는 데 자연스럽게 떠올랐어요.
나중에 다른분들의 코드를 보면서 송이님이 말씀하신 weakmap이 이렇게 사용할 수 있구나를 알게 되었습니다.ㅜ 다음에 비슷한 경우에 두 가지에 대해 생각해 볼 거 같습니다.
There was a problem hiding this comment.
이 if문 이렇게 두어도 괜찮은가..? 일단 전 한 함수 안에 if문 3개 이상이면 진절머리나는데요
if문으로 구현할 수 밖에 없는 구조라고 생각되기도 하고 너무너무 보기 안 좋은 것 같기도 하고..
공주님들은 어떻게 생각하시나요?
There was a problem hiding this comment.
이 부분은 앞서 준일 코치님이 리뷰하실 때 dispatch 썼던 부분으로 해결하셔도 좋을 것 같고,
저는 아래처럼 해결했어요!
저도 코드를 보기 좋게 짜야하는 병이 있거든요,,
if문을 많이 써야 하는 상황에는 조건문을 보기 쉽게 만든다거나, (저는 isClassName, isEvent 등의 유틸 함수를 사용했어요)
if 문 내부에서 처리해야 하는 내용을 따로 빼기도 합니다. (updateAttribute에서는 그닥 어울리지 않는 것 같지만ㅎㅎ)
There was a problem hiding this comment.
개인적으로는 작성해주신 코드가 함수 안에 if 문이 여러개여도 각 if문 안의 로직 처리가 깔끔하고 독립적이어서 가독성이 떨어진다는 생각이 들지 않습니다!
이와 별개로 각 분기마다 continue 처리도 해주시는군요! 생각 못해봤는데 배워감니다아👍
There was a problem hiding this comment.
이렇게 깔끔하게 함수 분리는 못 했지만 ㅠ 저도 if문 싫어 인간이라 넘 공감합니다 ..........,,!
반복적으로 검사할 조건이 있거나 큰 틀이 보이면 스위치도 자주 사용하고,,
각 if문이 해야하는 역할들이 다채로우면 개별 함수로 분리해도 좋을 거 같아용
과제 체크포인트
배포 링크
https://eveneul.github.io/front_6th_chapter1-2/
기본과제
가상돔을 기반으로 렌더링하기
이벤트 위임
심화 과제
Diff 알고리즘 구현
과제 셀프회고
기술적 성장
🗺️
mapVSWeakmapWeakmap을 사용할 일이 있다고 힌트를 주셨는데, eventManager.js 구현에 필요하다는 것을 알게 되었습니다.Weakmap에 대해 먼저 공부하고, 일반 Object와 map, 그리고 weakmap의 차이점을 먼저 습득하고 eventManager.js 구현에 나섰습니다.eventType(click, blur, change...)을 키로 삼고,{ element, handler }형태로 구성해 보려고 했지만,Weakmap은 반복문 사용이 불가능하다는 점을 알게 되어,map으로 대체하게 되었습니다.Map을 기반으로 한eventStore를 만들었고, 각 eventType에 대해 내부적으로 또 다른 Map을 사용해 element와 handler를 저장했습니다.Set을 사용해 중복 등록을 방지했습니다.코드 품질
newNodeChildren을 기준으로 비교하자니 삭제된 요소를 감지하지 못하고,oldNodeChildren기준으로 비교하자니 추가된 요소를 감지하지 못해서 둘 중 length가 더 긴 기준으로 비교하려고Math.max()를 사용했습니다.createElement.. 이대로 괜찮은 건가 싶습니다. if문 떡칠입니다. 한 함수에 3개 이상 if문 들어가는 거 안 좋아하는데, 여러 타입에 대응해야 하다 보니if문이 많이 쓰였습니다. 클린 코드 책을 미리 읽었으면 이런 참사도 없었겠지요..학습 효과 분석
node는 브라우저가 실제로 화면에 렌더링하는 요소의 최소 단위라는 것을 명확히 이해했습니다. HTML 요소, 텍스트, 주석 등 모든 DOM 구성 요소가Node객체로 표현된다는 점이 재미있었다.과제 피드백
renderElement.js항목만 테스트에 있어서updateElement.js는 구현 안 해도 기본 과제는 통과하는 건가? 했는데...... 마지막 두 개의 테스트에서 막혔습니다. 그리고 e2e 테스트도updateElement.js가 정상적으로 구현되어야 pass가 되어서 그냥 다 구현했습니다. 😭리뷰 받고 싶은 내용
eventManager.js에서 저는new Map()을 사용했지만,map이 아닌weakmap을 사용한다면 어떤 구조로 작성을 했어야 할까요?weakmap의 key는 오직 객체만 가능하고, 그 객체가 다른 곳에서 더 이상 참조되지 않으면 자동으로 가비지 컬렉션의 대상이 된다고 하지만, 반복문을 돌리지 못하는 것 때문에 구현에 어려움을 겪어map을 사용했지만 다른 분들은weakmap을 사용했다는 분들도 계셔서요. 코치님 같은 경우에는, 어떤 방식으로eventManager.js를 구현하셨을지 궁금합니다.