과제 체크포인트
배포 링크
https://chan9yu.github.io/front_6th_chapter1-2/
기본과제
가상돔을 기반으로 렌더링하기
- createVNode 함수를 이용하여 vNode를 만든다.
- normalizeVNode 함수를 이용하여 vNode를 정규화한다.
- createElement 함수를 이용하여 vNode를 실제 DOM으로 만든다.
- 결과적으로, JSX를 실제 DOM으로 변환할 수 있도록 만들었다.
이벤트 위임
- 노드를 생성할 때 이벤트를 직접 등록하는게 아니라 이벤트 위임 방식으로 등록해야 한다
- 동적으로 추가된 요소에도 이벤트가 정상적으로 작동해야 한다
- 이벤트 핸들러가 제거되면 더 이상 호출되지 않아야 한다
심화 과제
Diff 알고리즘 구현
- 초기 렌더링이 올바르게 수행되어야 한다
- diff 알고리즘을 통해 변경된 부분만 업데이트해야 한다
- 새로운 요소를 추가하고 불필요한 요소를 제거해야 한다
- 요소의 속성만 변경되었을 때 요소를 재사용해야 한다
- 요소의 타입이 변경되었을 때 새로운 요소를 생성해야 한다
과제 셀프회고
1주차보다는 비교적 순탄했던? 2주차였습니다. 하지만 1주차보다 얻어간 내용들이 많아서 좋았어요. Virtual DOM이 무엇인지 왜 쓰는지에 대해 깊이 고민할 수 있었던 시간이었습니다.
그리고 이번주차는 팀원분들이 시간을 내서 과제한 내용을 정리하고 서로 공유해보자고 제안해주셨고, 덕분에 틈틈히 정리를 해봤는데 정말 도움이 많이 되었던거 같습니다 정말 5팀 분들이 서로 도와주고 열심히 하는 모습을 보면서 덩달아 저까지 자극받고 열심히 하게 되었습니다 🙂
현재까지 정리한 내용들:
- https://github.com/chan9yu/front_6th_chapter1-2/issues/4
- https://github.com/chan9yu/front_6th_chapter1-2/issues/5
- https://github.com/chan9yu/front_6th_chapter1-2/issues/8
(시간이 된다면 나머지 내용도 정리를 할 예정입니다)
기술적 성장
이번 주차 메인 기능인 Virtual DOM에 대해서 좀 더 딥하게 알 수 있었고, JSX를 사용만 했던 관점을 벗어나 리액트가 어떤 고민을 했고 어떤 생각으로 구현했는지 많이 느껴졌던 것 같습니다.
처음 createVNode를 구현할 때는 엄청 막막했지만, 직접 하나씩 타입을 분기, 자식 노드를 평탄화, 함수형 컴포넌트와 일반 엘리먼트, 텍스트 노드 등 다양한 케이스를 처리하다 보니 JSX가 실제로 어떻게 객체로 변환되는지 이해할 수 있었습니다.
updateElement로 diff & patch를 구현하면서 DOM 업데이트의 효율성과 일관성을 위해 어떤 구조와 로직이 필요한지 고민하게 되었고, 실제로 불필요한 렌더링을 줄이기 위한 포인트(?)도 자연스럽게 이해할 수 있었어요
이벤트 위임 방식의 구현을 통해 동적으로 추가/제거되는 엘리먼트에서도 이벤트가 일관되게 동작하도록 만드는 것이 얼마나 중요한지 직접 경험했습니다.
코드 품질
이번에 구현하면서 개인적으로 고민을 많이했던 부분입니다. 바로 이벤트 위임의 구현인데요
/**
* 루트 요소에 이벤트 위임 리스너를 설정
* 등록된 모든 이벤트 타입에 대해 위임 방식으로 이벤트를 처리
*
* @param {HTMLElement} rootElement - 이벤트 위임의 루트 요소
*/
export function setupEventListeners(rootElement) {
eventMap.forEach((handlers, eventType) => {
rootElement.addEventListener(eventType, (event) => {
let currentTarget = event.target;
// 이벤트가 발생한 요소부터 루트까지 버블링하면서 핸들러 찾기
while (currentTarget && currentTarget !== rootElement) {
const elementHandlers = handlers.get(currentTarget);
if (elementHandlers) elementHandlers.forEach((handler) => handler(event));
// 부모 요소로 이동하여 버블링 효과 구현
currentTarget = currentTarget.parentElement;
}
});
});
}
이벤트 위임 방식으로 이벤트를 한 번만 등록하고, 각 엘리먼트별로 핸들러를 효율적으로 관리할 수 있도록 구현하였습니다.
리액트가 왜 이벤트 위임을 사용했는지 이번을 통해서 조금은 느껴졌는데, 제 생각에는 동적으로 추가/제거되는 엘리먼트에서도 일관되게 이벤트가 동작하도록 하기 위함인 것 같다는 생각이 들었습니다.
학습 효과 분석
이번 과제를 통해 Virtual DOM의 구조와 동작 원리를 직접 구현해보면서, 단순히 리액트의 사용법이 아니라 "왜 이런 구조가 필요한지", "어떻게 동작하는지"를 깊이 이해할 수 있었던 거 같습니다
특히, JSX가 실제로 어떻게 객체로 변환되고, diff와 patch 과정에서 어떤 기준으로 DOM이 갱신되는지 직접 코드를 작성하며 배울 수 있었던 점이 가장 좋았습니다
기본적인 Virtual DOM과 이벤트 위임, 속성 처리 등은 이해했지만, 실제 리액트처럼 더 복잡한 최적화
- key 기반 리스트 diff
- memoization
- 비동기 렌더링 에 대해서는 더 깊이 공부를 해봐야 겠다는 생각이 들었습니다.
과제 피드백
1주차때보다 많이 얻어갔다고 생각하는 과제입니다. 좋은 과제 감사합니다. 3주차때도 기대가 됩니다!
리뷰 받고 싶은 내용
코드 구현부에 대해서 클린코드 관점으로 리뷰를 받아보고 싶습니다. updateElement.js 부분인데, 함수 내용이 많다고 생각이 들고 여러 가지 동작을 처리하고 있다고 생각됩니다.
나름 함수 분리도 시도해봤지만 오히려 가독성이 안 좋고 코드 읽기가 힘들어지는 것 같습니다. 어떤 방식으로 코드를 구현해야 읽는 사람 입장에서 쉽게 읽혀질 수 있는지 여쭙고 싶습니다.
그리고 구현을 하다보니
if (
typeof newNode !== typeof oldNode ||
(typeof newNode === "string" && typeof oldNode !== "string") ||
(typeof newNode !== "string" && typeof oldNode === "string") ||
(isVNode(newNode) &&
isVNode(oldNode) &&
(newNode.type !== oldNode.type || newNode.props?.key !== oldNode.props?.key))
)
이런식으로 조건문이 복잡해지는 경우가 있는데 이런 복잡한 조건문을 더 읽기 쉽고 의도가 명확한 형태로 리팩토링할 수 있는 방법이 있는지, 아니면 저렇게 하는 게 최선인지도 궁금합니다.
과제 피드백
고생하셨습니다 찬규님! 5팀만의 규칙으로 이슈에 정리해주셨던 것 같은데 너무 잘 정리해주셨네요. :+1 어떤 과정으로 과제를 진행해주셨는지 명확하게 이해할 수 있었습니다. 말그대로 왜 이 구조를 사용하는지, 어떻게 동작하는지 이해할 수 있는 좋은 기회였다고 생각하고 실제 리액트 구조에서는(리스트업 해주신 것 처럼) 이거보다 더 복잡한 여러 과정들이 있으니 이참에 공부 해보고 공유해주시는 것도 좋을 것 같아요 ㅎㅎ
이어서 질문 남겨주신 부분 답변 드려보면,
update element의 클린코드 관점
음..지금 함수의 문제점이라면 한 함수에서 너무 많은 종류의 속성을 처리하는거지 않나 싶긴 한데요. 속성 타입별로 함수를 만드는 것도 좋겠지만, 공통적으로 처리할 수 있는 함수를 뽑아내는 방식으로 리팩터링을 하는게 좋지 않을까 싶습니다! 분기에 대한 것들도 함수로 명확하게 의도를 드러내고 동작에 대한것도 함수로 분리해서 명확하게 의도를 드러내구요. 예를 들어서 hasDifferentType해서 oldNode의 타입과 newNode의 타입을 비교하는 함수를 분리하면서 작게작게 함수들을 대체하면서 분리하는거죠. 이런 형태로 분리하다보면 함수 이름 자체로 의도가 드러나기 때문에 좀 더 가독성이 좋아지는 부분이 생기는 것 같습니다. 추가로 분기에 대한 중첩도 깊어질 수 있으니 일반적으로 말하는 early return, guard clause 패턴을 사용해서 걷어내는 것도 가독성에 좋을 것 같아요.
고생하셨고 다음 주 과제도 화이팅입니다!