왓에버

Virtual DOM을 실제 DOM으로 렌더링 하기

choijming21 2025. 2. 5. 19:21

1. 프로젝트 구조

📦src
 ┣ 📂types
 ┃ ┗ 📜createElementTypes.ts  // 타입 정의
 ┣ 📜App.tsx                  // 메인 컴포넌트, 진입점
 ┣ 📜jsx.ts                   // createElement 구현
 ┣ 📜render.ts               // Virtual DOM → 실제 DOM 변환
 ┗ 📜style.css               // 스타일

 

 

 

 

2. Virtual DOM 렌더링 함수(render.ts)

렌더링 함수는 Virtual DOM을 실제 DOM으로 변환하는 핵심 기능을 담당합니다.

import { VirtualElement } from "./types/createElementTypes";

export function render(vdom: VirtualElement | string, container: HTMLElement) {

 

함수는 두 개의 인자를 받습니다.

 

  • vdom: Virtual DOM 객체 또는 문자열
  • container: 실제 DOM 요소 (렌더링될 위치)

 

2.1 문자열 처리

if (typeof vdom === "string") {
  container.appendChild(document.createTextNode(vdom));
  return;
}

 

 

텍스트 내용을 처리합니다. 예를 들어, "Hello"와 같은 문자열을 DOM의 텍스트노드로 변환합니다.

 

 

2.2 DOM 요소 생성

const element = document.createElement(vdom.type as string);

 

Virtual DOM의 type 속성을 사용하여 실제 HTML 요소를 생성합니다.

ex) "div" => <div></div>

 

 

2.3 속성(props) 처리

Object.entries(vdom.props).forEach(([key, value]) => {
  if (key === "className") {
    element.setAttribute("class", value as string);
  } else {
    element.setAttribute(key, value as string);
  }
});

 

Virtual DOM의 props를 실제 DOM 요소의 속성으로 변환합니다.

 

  • className은 특별히 class 속성으로 변환
  • 나머지 props는 그대로 속성으로 설정

 

2.4 자식 요소 처리

vdom.children.forEach((child) => {
  render(child, element);
});

 

 

모든 자식 요소에 대해 재귀적으로 render함수를 호출하여 전체 DOM 트리를 구성합니다.

 

 

 

 

3. 컴포넌트 구현(App.tsx)

3.1 Header 컴포넌트

function Header({ title, buttonText }: HeaderProps): VirtualElement {
  return (
    <div className="header">
      <h1 id="title">{title}</h1>
      <button id="login">{buttonText}</button>
    </div>
  );
}

 

3.2 Content 컴포넌트

function Content({ text }: ContentProps): VirtualElement {
  return (
    <div className="container">
      <p id="content">{text}</p>
    </div>
  );
}

 

3.3 App 컴포넌트: 전체 앱의 구조를 정의하는 메인 컴포넌트 입니다.

function App(): VirtualElement {
  return (
    <div id="app">
      <Header title="Header" buttonText="login" />
      <h1>Hello, React!</h1>
      <Content text="Virtual DOM 이해 및 복잡한 구조의 컴포넌트 분석" />
    </div>
  );
}

 

 

 

 

4. 앱 렌더링: Virtual DOM을 생성하고 실제 DOM으로 렌더링합니다.

const appElement = <App />;
const container = document.getElementById("app");

if (container) {
  render(appElement, container);
}

 

 

 

 

5. Virtual DOM 구조 확인: 생성된 Virtual DOM의 구조를 콘솔에서 확인할 수 있습니다.

console.log(JSON.stringify(appElement, null, 2));

 

 

 

 

6. 전체 코드

render.ts

import { VirtualElement } from "./types/createElementTypes";

export function render(vdom: VirtualElement | string, container: HTMLElement) {
  // 문자열이나 숫자인 경우 테스트 노드 생성
  if (typeof vdom === "string") {
    container.appendChild(document.createTextNode(vdom));
    return;
  }

  // 1. 실제 DOM 엘리먼트 생성
  const element = document.createElement(vdom.type as string);

  // 2. 속성(props) 처리
  Object.entries(vdom.props).forEach(([key, value]) => {
    // className 특별 처리
    if (key === "className") {
      element.setAttribute("class", value as string);
    } else {
      element.setAttribute(key, value as string);
    }
  });

  // 3. 자식 요소를 재귀적으로 처리
  vdom.children.forEach((child) => {
    render(child, element);
  });

  // 4. 부모 컨테이너에 추가
  container.appendChild(element);
}

 

App.tsx

/** @jsx createElement */
import "./style.css";
import {
  ContentProps,
  HeaderProps,
  VirtualElement,
} from "./types/createElementTypes";
import { createElement } from "./jsx";
import { render } from "./render";

// Header 컴포넌트
function Header({ title, buttonText }: HeaderProps): VirtualElement {
  return (
    <div className="header">
      <h1 id="title">{title}</h1>
      <button id="login">{buttonText}</button>
    </div>
  );
}

// Content 컴포넌트
function Content({ text }: ContentProps): VirtualElement {
  return (
    <div className="container">
      <p id="content">{text}</p>
    </div>
  );
}

// App 컴포넌트
function App(): VirtualElement {
  return (
    <div id="app">
      <Header title="Header" buttonText="login" />
      <h1>Hello, React!</h1>
      <Content text="Virtual DOM 이해 및 복잡한 구조의 컴포넌트 분석" />
    </div>
  );
}

const appElement = <App />;
const container = document.getElementById("app");

if (container) {
  render(appElement, container);
}

console.log(JSON.stringify(appElement, null, 2));

 

 

 

 

7. 결론

이 구현을 통해 React의 핵심 개념인 Virtual DOM과 컴포넌트 기반 구조를 이해할 수 있었습니다. 실제 React는 더 복잡하고 최적화된 방식으로 동작하지만, 이 기본 구현으로도 React의 작동 원리를 파악할 수 있었습니다.

 

주요 포인트

 

  • JSX는 createElement 함수 호출로 변환됨
  • Virtual DOM은 실제 DOM의 가벼운 복사본
  • render 함수가 Virtual DOM을 실제 DOM으로 변환
  • 컴포넌트는 재사용 가능한 UI 단위

이러한 이해를 바탕으로 React와 같은 현대적인 UI 라이브러리의 동작 방식을 더 깊이 이해할 수 있습니다.