개발 공부/React

김민태 프론트엔드 강의 - javascript로 React 만들기

차정 2023. 3. 11. 12:27

 

React 없이, JSX를 직접 쓰고, 가상 DOM을 만들고,
그걸 진짜 DOM으로 만들어서 화면에 그리는 기능을 직접 구현한다.

 

구성 요소 역할

createElement JSX를 가상 DOM 객체로 변환함
createDOM 가상 DOM 객체를 실제 DOM 요소로 변환
render 만들어진 DOM을 브라우저에 붙임

 

//JSX를 가상 DOM 객체로 변환함
export function createVNode(tag, props, ...children) {
  return { tag, props, children };
}

//가상 DOM 객체를 실제 DOM 요소로 변환
export function createDOM(node) {
  if (typeof node === 'string') {
    return document.createTextNode(node);
  }

  const element = document.createElement(node.tag);

  Object.entries(node.props)
    .forEach(([name, value]) => element.setAttribute(name, value));

  node.children
    .map(createDOM)
    .forEach(element.appendChild.bind(element));

  return element;
}

//	만들어진 DOM을 브라우저에 붙임
export function render(vdom, container) {
  container.appendChild(createDOM(vdom));
}

const Title = <div><h1>Hello</h1></div>

render(<Title/>, document.querySelector('#root'));



1단계 - JSX로 코드 작성

/* @jsx createVNode */
const Title = () => <div><h1>Hello</h1></div>;

*@jsx 주석 지시어 를 통해 바벨로 하여금 JSX를 어떤 함수로 변환할지 설정함.
React.createElement 대신 우리가 만든 createVNode 함수를 써야 하니까,
Babel에게 createVNode 사용하라고 알려주기 위해 @jsx 주석 지시어 사용.
(React 16 이전에는 ) 바벨이  import React from "react"; 통해서
jsx를  const element = React.createElement("h1", null, "Hello"); 변환과정 거쳤음.
React 17 이후에는 import 안해도 자동으로 react 패키지의 jsx-runtime 모듈에 정의된 jsx() 함수"를 Babel이 자동으로 import해서 

const el = _jsx("h1", { children: "Hello" });

 

React.createElement를 대체함 

 

 

JSX 코드

import React from "react"; /* 17 이전에는 React.createElement가 동작해야하므로 import React 필요*/

const element = <div><h1>Hello</h1></div>;

 

바벨 변환결과

const element = createVNode(
  "div",
  null,
  createVNode("h1", null, "Hello")
);


//JSX를 가상 DOM 객체로 변환함
export function createVNode(tag, props, ...children) {
  return { tag, props, children };
}
  • React에서 컴포넌트는 반드시 함수여야 하고, 대문자로 시작해야 한다.
    이 경우 Babel이 내부적으로 대문자로 시작하는 JSX 태그를 일반 문자열이 아니라 JavaScript 변수(컴포넌트)로 취급하여 React.createElement를 호출한다.
  • tag가 함수 컴포넌트인지 확인 (typeof tag === 'function').
    • 함수 컴포넌트 라면 tag(props)를 호출하여 실행 결과를 반환
  • children이 1개면 단일 값으로 전달, 2개 이상이면 배열로 전달.
    • <Title>이름</Title>
    • <Title><Item>1</Item><Item>2</Item></Title> 구분

//jsx 코드로부터 변환된 vdom
const element = {
  tag: "div",
  props: null,
  children: [
    {
      tag: "h1",
      props: null,
      children: ["Hello"]
    }
  ]
};




render(<Title/>, document.querySelector('#root'));


이것은 즉 

render(  VDOM, document.querySelector('#root')); 인데
render함수는 vDom과 container를 받아 vDom 을 Dom으로 만들어 돔에 붙이는 함수임.

//	만들어진 DOM을 브라우저에 붙임
export function render(vdom, container) {
  container.appendChild(createDOM(vdom));
}

 

//가상 DOM 객체를 실제 DOM 요소로 변환
export function createDOM(node) {
  if (typeof node === 'string') {
    return document.createTextNode(node);
  }

  const element = document.createElement(node.tag);

  Object.entries(node.props)
    .forEach(([name, value]) => element.setAttribute(name, value));

  node.children
    .map(createDOM)
    //.forEach(element.appendChild.bind(element));
    .forEach(child => element.appendChild(child));
  return element;
}

 

vdom.children
  .map(createDOM)  // 자식들을 DOM으로 변환 → [<p>, <span>]
  .forEach(child => element.appendChild(child));  // <div>에 붙이기


핵심 요약
.map(createDOM) → VDOM 배열 → DOM 배열

.forEach(...) → 그 배열을 현재 요소에 붙이는 작업