과제 체크포인트
배포 링크
https://jeongmingi123.github.io/front_6th_chapter1-2/기본과제
가상돔을 기반으로 렌더링하기
- createVNode 함수를 이용하여 vNode를 만든다.
- normalizeVNode 함수를 이용하여 vNode를 정규화한다.
- createElement 함수를 이용하여 vNode를 실제 DOM으로 만든다.
- 결과적으로, JSX를 실제 DOM으로 변환할 수 있도록 만들었다.
이벤트 위임
- 노드를 생성할 때 이벤트를 직접 등록하는게 아니라 이벤트 위임 방식으로 등록해야 한다
- 동적으로 추가된 요소에도 이벤트가 정상적으로 작동해야 한다
- 이벤트 핸들러가 제거되면 더 이상 호출되지 않아야 한다
심화 과제
Diff 알고리즘 구현
- 초기 렌더링이 올바르게 수행되어야 한다
- diff 알고리즘을 통해 변경된 부분만 업데이트해야 한다
- 새로운 요소를 추가하고 불필요한 요소를 제거해야 한다
- 요소의 속성만 변경되었을 때 요소를 재사용해야 한다
- 요소의 타입이 변경되었을 때 새로운 요소를 생성해야 한다
과제 셀프회고
기술적 성장
virtual dom을 학습하며, 항상 기본기가 많이 부족하다고 생각했는데, 이번 과제를 통해 많이 배우게 되었다고 생각합니다.재귀함수를 안짜본지 너무 오래되어서, 구현하는데 너무 애를 먹었습니다. 하지만 이번에 과제를 구현하면서 저에게 많은 도움이 되었다고 생각합니다.
코드 품질
사실 너무 코드 리팩토링 할 부분이 많아....... 이번엔 따로 첨부를 안하겠습니다 ㅜㅜ학습 효과 분석
아무래도 virutal dom을 학습하면서 많은 배움이 되었다고 생각합니다. 가상 노드를 만들어 보고, eventManger 및 element도 만들어보면서 기본기를 기를수 있어 가장 큰 배움이 있지 않았나 생각합니다. 또한 준일님 블로그를 보며, 순수 javascript를 사용하여 react에 있는 상태 관리 라이브러리를 만드시는 것을 보며 정말 기본기가 중요하다고 뼈저리게 느꼈습니다. 이런 부분을 이해해야 나중에 실무에서 문제가 발생했을 때 바로바로 해결이 가능하지 않나 생각이 듭니다.과제 피드백
과제가 어려워서 오히려 좋았습니다. 이런 기초적인 동작 원리를 구현하면서 많은 도움이 되었다고 생각합니다.리뷰 받고 싶은 내용
클린코드 관점에서 질문드립니다. createElement 를 구현하면서 뎁스가 길어진다고 생각하여, 함수로 쪼걨는데, 너무많이 쪼갰나 생각이 들었습니다. 이런식으로 하는게 괜찮은 방법일까요? 아니면 추가적으로 보완해야한다면 어떤식으로해야할지 질문드립니다../**
* 가상 노드를 실제 DOM 요소로 변환
* @param {any} vNode - 변환할 가상 노드
* @returns {Node} - 생성된 DOM 노드
*/
export function createElement(vNode) {
// null, undefined, boolean 처리
if (isNullOrBoolean(vNode)) {
return createEmptyTextNode();
}
// 문자열이나 숫자 처리
if (isPrimitive(vNode)) {
return createTextNode(vNode);
}
// 배열 처리
if (Array.isArray(vNode)) {
return createFragmentFromArray(vNode);
}
// 객체 형태의 가상 노드 처리
return createElementFromVNode(vNode);
}
/**
* null, undefined, boolean 값인지 확인
*/
function isNullOrBoolean(value) {
return value == null || typeof value === "boolean";
}
/**
* 원시 타입(문자열, 숫자)인지 확인
*/
function isPrimitive(value) {
return typeof value === "string" || typeof value === "number";
}
/**
* 빈 텍스트 노드 생성
*/
function createEmptyTextNode() {
return document.createTextNode("");
}
/**
* 텍스트 노드 생성
*/
function createTextNode(value) {
return document.createTextNode(String(value));
}
/**
* 배열로부터 DocumentFragment 생성
*/
function createFragmentFromArray(vNodeArray) {
const fragment = document.createDocumentFragment();
vNodeArray.forEach((child) => {
const childElement = createElement(child);
fragment.appendChild(childElement);
});
return fragment;
}
/**
* 가상 노드로부터 DOM 요소 생성
*/
function createElementFromVNode(vNode) {
const { type, props = {}, children = [] } = vNode;
if (typeof type === "string") {
return createHTMLElement(type, props, children);
}
if (typeof type === "function") {
throw new Error("함수형 컴포넌트는 createElement로 직접 처리할 수 없습니다. 먼저 normalizeVNode로 정규화하세요.");
}
return createEmptyTextNode();
}
..... 이외
과제 피드백
안녕하세요 민기님~ 2주차 과제 잘 진행해주셨네요!! 고생하셨습니다. 다만, 과제 PR을 제출해주셔야 되는데 repo 링크를 제출해주셨네요 ㅋㅋ 다음에는 PR 링크로 제출 부탁드려요!
클린코드 관점에서 질문드립니다. createElement 를 구현하면서 뎁스가 길어진다고 생각하여, 함수로 쪼걨는데, 너무많이 쪼갰나 생각이 들었습니다. 이런식으로 하는게 괜찮은 방법일까요? 아니면 추가적으로 보완해야한다면 어떤식으로해야할지 질문드립니다..
오 ㅋㅋㅋ 저는 이런 방식을 더 선호하는 편입니다. 이걸 저는 "코드의 추상화 수준을 맞춘다" 라고 표현하곤 해요. 코드를 읽을 때 가독성이 훨씬 좋아지고, 함수의 이름을 통해 어떤 동작을 하는지 예측이 쉬워지는 효과가 있어요!
조금 더 나아가자면, 이렇게 표현할 수 있도록 함수를 하나 더 만들면 좋지않을까 싶네요!
export function createElement = pipe(
[isNullOrBoolean, createEmptyTextNode],
[isPrimitive, createTextNode],
[Array.isArray, createFragmentFromArray],
createElementFromVNode, // 혹은 [()=> true, createElementFromVNode]
)
저는 이런걸 "선언형 프로그래밍으로 만들었다"고 표현해요. if, for 같은 코드 대신 함수로 최대한 랩핑하여 코드가 어떤 형태를 갖춰야 하는지 선언하는거죠 ㅎㅎ
고생하셨습니다~!