본문 바로가기

Front/향해플러스

Virtual DOM 만들기 - #2. normalizeVNode (향해 플러스 2주차)

normalizeVNode

가상 DOM 객체가 만들어졌다면, 이제는 이걸 정제하는 과정이 필요하다. JSX에서 오는 다양한 형태(null, boolean, 함수 등)를 통일된 포맷으로 유지하도록 보정한다.


1. `null` ,`undefined`, `true`, `false` → 빈 문자열로 변환

if (vNode === null || vNode === undefined || typeof vNode === "boolean") {
  return "";
}

 

예시

{isShow && <div>Hello</div>}
// isShow가 false면 normalizeVNode(false)가 들어오는 걸 ""로 변환

2. 문자열과 숫자는 문자열로 변환

 if (typeof vNode === "string" || typeof vNode === "number") {
   return String(vNode);
 }

 

예시

<p>Hello</p> // normalizeVNode("Hello") -> "Hello"
<p>42</p> // normalizeVNode(42) -> "42"

3. vNode가 함수인 경우, 함수를 실행 후 normalizeVNode 재귀 호출

if (typeof vNode.type === "function") {
 return normalizeVNode(vNode.type({ ...vNode.props, children: vNode.children }));
}

 

예시

function Component(props) {
	return createVNode("h1", null, `Hi ${props.name}`);
}

normalizeVNode(createVNode(Component, { name: "sangsoo" }));
// 결과
{
	type: "h1",
	props: null,
	children: ["Hi sangsoo"]
}

함수 컴포넌트는 `vNode.type` 이 함수이므로 해당 함수를 실행 후 결과를 다시 `normalizeVNode` 를 재귀 호출 한다.

 


4. 배열인 vNode의 children 들을 재귀적으로 정규화한다

  // vNode의 자식 vNode들을 재귀적으로 정규화한다.
  if (Array.isArray(vNode.children)) {
    const noramlizedChildren = vNode.children
      .map(normalizeVNode)
      .filter((child) => child !== "" && child !== null && child !== undefined && child !== false);
    return {
      ...vNode,
      children: noramlizedChildren,
    };
  }
normalizeVNode(
	createVNode("div", null, [
		null,
		false,
		"Hi",
		42,
		createVNode("span", null, "child"),
		[undefined, "deep"]
	])
)
// 결과
{
	type: "div",
	props: null,
	children: [
		"Hi",
		"42",
		{ type: "span", props: null, children: ["child"] },
		"deep"
	]
}
  • 중첩 배열 평탄화
  • `null`, `false`, `undefined` 제거
  • 숫자 → 문자열로 변환
  • `span` 은 그대로 유지

 


마무리

`normalizeVNode`는 데이터의 불필요한 잡음을 없애고, 렌더링 로직이 안정적으로 동작하도록 보장한다. 이 단계를 거치면 이후의 createElement나 diffing 로직이 훨씬 단순해진다.

 

다음 글에서는 이렇게 정재된 vNode를 실제 DOM으로 변환하는 `createELement`를 구현해보자!