React.createElement와 React.render

jh·2023년 11월 26일

참고
리액트 공식문서

  • 해당 글은 리액트 하위버전을 예시로 설명하고 있습니다.

JSX는 Syntax Sugar다

Babel은 JSX를 React.createElement() 호출로 컴파일합니다

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

해당 JSX 문법으로 작성 된걸 Babel에서는

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

React.createElement() 로 바꿔서 컴파일 해준다는 의미이다
-> 다시 말해 JSX는 React.createElement() 를 조금 편하게 호출할 수 있는 syntax sugar일 뿐

JSX는 XSS 공격을 방지할 수 있다

기본적으로 React DOM은 JSX에 삽입된 모든 값을 렌더링하기 전에 이스케이프 하므로, 애플리케이션에서 명시적으로 작성되지 않은 내용은 주입되지 않습니다. 모든 항목은 렌더링 되기 전에 문자열로 변환됩니다. 이런 특성으로 인해 XSS (cross-site-scripting) 공격을 방지할 수 있습니다.

React.createElement()

일반 JS에서도 document.createElement() 가 존재하는데, 둘의 차이는 뭘까?

const element = document.createElement('div')
element instanceof HTMLElement // true

document.createElement() 의 결과는 HTMLElement 이다
생성된 element를 직접 DOM에 삽입하여 화면을 그릴 수 있다

const element = React.createElement(type, props, ...children)

하지만 React.createElement() 로 생성된 것은 객체이다

  • 해당 객체는 불변함(속성 수정 X)

리액트에서는 해당 객체를 React 엘리먼트 라고 하며, 화면에서 보고 싶은 것을 나타내는 표현이라 생각하면 된다. React는 이 객체를 읽어서, DOM을 구성하고 최신 상태로 유지하는 데 사용한다

  • 브라우저 DOM 엘리먼트와 달리 React 엘리먼트는 일반 객체이며(plain object) 쉽게 생성할 수 있습니다
  • React DOM은 React 엘리먼트와 일치하도록 DOM을 업데이트합니다

React element 렌더링

CRA로 리액트 개발환경을 생성하게 되면

//index.html
<div id="root"></div>
import { createRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = createRoot(domNode);
root.render(<App/>)

해당 로직이 index.js 같은 파일에 추가되어있는 걸 볼 수 있는데
여기서 createRoot()render() 라는 메서드를 사용하고 있다.

createRoot 는 React에서 제공하는 함수 중 하나로,
브라우저의 DOM 노드 내에서 React 컴포넌트를 표시할 수 있는 루트를 생성하는 데 사용됩니다

생성된 Root Node는 React에 의해 해당 노드 내부의 DOM을 관리하는 역할을 합니다.

render() 는 전달받은 리액트 엘리먼트들을 해당 Root Node(#root)의 하위 노드에 추가하면서 화면에 보여지게 됩니다

요약하자면 ,

  • 리액트는 createRoot() 를 통해 하나의 DOM Element를 Root Node 로 만든다(하나의 엔트리 포인트(진입점)를 잡는다고 생각하면 이해가 편함)

  • 이후 render() 를 통해 넘겨받은 여러 컴포넌트들을 Root Node 의 하위 노드로 추가해주면서, 화면에 출력된다

Root Node 내부에 존재하는 모든 DOM 엘리먼트는 렌더링이 호출되면 전부 교체되고, 이후의 호출들은 효율적인 갱신을 위해 React의 DOM 비교 알고리즘(Diffing)을 사용합니다.
createRoot() 는 넘겨받은 컨테이너 노드를 수정하지 않으며,
오직 그 자식만을 수정합니다.
기존 DOM 노드에 이미 존재하는 자식에 대해 덮어쓰기 없이 컴포넌트를 추가하는 것이 가능할 수도 있습니다.

번외

<html>
  <head><title>My app</title></head>
  <body>
    <nav id="navigation"></nav>
    <main>
      <p>!이 태그는 리액트로 관리 안해도 됨!</p>
      <section id="comments"></section>
    </main>
  </body>
</html>
const navDomNode = document.getElementById('navigation');
const navRoot = createRoot(navDomNode); 
navRoot.render(<Navigation />);

const commentDomNode = document.getElementById('comments');
const commentRoot = createRoot(commentDomNode); 
commentRoot.render(<Comments />);

이런 식으로 기존에 존재하는 html은 그대로 유지하고 싶다면, 필요한 태그에만 createRoot 를 사용하는 방법도 가능하다

0개의 댓글