
실제 리액트의 코드는 이와 다릅니다.
제가 작성한 코드는 학습하기 위해 작성한 코드임을 미리 알립니다!
전체 코드는 깃허브 에서 확인 해보실 수 있습니다.
이전 에는 createElement 함수를 만들어서 JSX를 js 코드로 변환해 VirtualDOM을 생성 했었습니다. 이번에 할건 그렇게 만들어진 VirtualDOM을 실제로 렌더링 해보겠습니다.
앞으로 VirtualDOM은 VDOM으로 줄여서 부르도록 하겠습니다.
지금 코드의 엔트리는 main.tsx 입니다.
그래서 여기에서 렌더링 해주는 코드를 작성해주면 돔에 잘 붙을겁니다.
한번 어떻게 만들지 생각을 해볼까요?
현재 VDOM은 계층적인 Object로 만들어져 있습니다. 아래처럼 말이죠!
그래프로 표현된 Object가 있으니 이 그래프를 dfs로 돌며 HTMLElement를 만들어 주면 될겁니다.
{
"tag": "div",
"props": {
"check": true
},
"children": [
{
"tag": "div",
"props": null,
"children": [
"first child"
]
},
{
"tag": "div",
"props": null,
"children": [
"Hello, JSX!"
]
}
]
}
// /lib/React/createDOM.ts
interface VirtualDomNode {
tag: string;
props?: any;
children: VirtualDomNode[];
}
type VirtualDomTextNode = string | number | boolean;
type VirtualDom = VirtualDomNode | VirtualDomTextNode;
type RealDom = HTMLElement;
// type predicates를 사용해 타입 단언하기
function checkVirtualDomTextNode(
element: VirtualDomNode | VirtualDomTextNode
): element is VirtualDomTextNode {
return (
typeof element === "string" ||
typeof element === "boolean" ||
typeof element === "number"
);
}
export default function createDOM(element: VirtualDom): RealDom | Text {
// 현재 element가 textNode인지 확인한다.
if (checkVirtualDomTextNode(element)) {
//textNode일 경우 현재 element를 그대로 반환한다.
return document.createTextNode(element.toString());
}
// tag로 Element를 생성한다.
const dom: RealDom = document.createElement(element.tag);
// props가 존재 할 경우 실제 element에 이를 추가한다.
if (element.props) {
Object.keys(element.props).forEach((key) => {
(dom as any)[key] = element.props[key];
});
}
// 자식들을 재귀적으로 돌며 위 과정을 반복한다.
element.children.forEach((child: VirtualDom) => {
dom.append(createDOM(child));
});
return dom;
}
이제 createDOM 함수를 만들었으니 이를 실제로 사용해봅니다.
// /src/App.tsx
import React from "@core/React";
function App() {
return (
<div>
<div>Hello, JSX!</div>
<div>second child!</div>
</div>
);
}
export default App;
// /src/main.tsx
import React from "@/lib/React";
import App from "./App";
const root = document.querySelector("#root");
const dom = React.createDOM(<App />);
root?.appendChild(dom);
createDOM이 반환하는 값이 실제돔 이기에 그냥 이걸 appendChild 해주기만 하면 됩니다!
그러면 아래처럼 렌더링이 잘 되는 모습을 확인할 수 있습니다.

리렌더링은 보통 상태가 변경되었을 때 일어납니다. 이를 위해서는 useState 훅을 구현해야 할겁니다. 다른 방법도 존재하겠지만 목표는 리액트를 최대한 비슷하게 따라하는 것 이니까요! 그래서 다음글에서는 useState 훅도 구현해보고, 리렌더링이 일어나는 과정도 구현해볼 생각입니다.