091

[JS/React] JSX(2): Component, Element 본문

Programming Language/JavaScript&TypeScript

[JS/React] JSX(2): Component, Element

공구일 2026. 4. 10. 18:13
728x90

2. Component&React Element

(1) 컴포넌트(Component)란 UI를 반환하는 함수이고 props를 받아 엘리먼트 트리를 동적으로 생성해 내는 팩토리 함수로, 일반 함수와 거의 같고 2개의 추가적인 규칙이 추가된 형태입니다.

import React from "react";

function Book(props){
    return (
        <div>
            <h1>{`이 책의 이름은 ${props.name}입니다.`}</h1>
            <h2>{`이 책은 총 ${props.numberOfPage}페이지로 이루어져 있습니다.`}</h2>
        </div>
        //<div></div> - JSX expressions must have one parent element.
    )
}

export default Book;
import React from "react";
import Book from "./Book";

function Library(props){
    return (
        <div>
            <Book name="처음 만난 파이썬" numberOfPage={300} />
            <Book name="처음 만난 AWS" numberOfPage={400} />
            <Book name="처음 만난 리액트" numberOfPage={500} />
        </div>
    )
}

export default Library;

 

-> 컴포넌트는 꼭 대문자로 해줘야하는 이유는 React가 HTML 태그와 컴포넌트를 대/소문자로 구분하기 때문입니다. 

-> props란 부모가 자식에게 데이터를 전달하는 것으로, 부모(Library)가 attribute로 던져주면, 자식(Book)이 props parameter로 값을 받습니다. 데이터를 속성값으로 넘겨줄 때, 문자열만을 ""로 넘기고, 나머지 숫자나 불린, 변수값들은 전부 {} 중괄호에 감싸줘야합니다.

• props는 읽기 전용으로 절대 자식에서 수정할 수 없습니다. 그리고 언제나 부모에서 자식으로 단방향으로만 흐릅니다.

위처럼 props 객체로 전달받은 다음, props.name, props.numberOfPage로 접근해도 되지만 이전에 학습했던 구조분해식을 props 대신 {name, numberOfPage}를 작성해서 받아도 됩니다.

 

(2) 리액트 엘리먼트(react element)는 React.createElement()가 반환하는 순수 JS 객체로, type, props,children 세 필드로 구성된 불변(immutable) 데이터 구조를 가지고 있습니다.

- 원래 엘리먼트(dom element)는 웹 사이트의 정보를 담고 있는 객체인 DOM의 용어이며, 이 리액트 앨리먼트는 브라우저가 읽어들이는 실제 DOM 노드가 아니라, 단순히 화면의 특정 상태를 설명하기 위해 메모리 상에 가볍게 존재하는 JS 객체로, 실제 DOM에 비해 생성하고 파괴하는데 드는 연산량이 압도적으로 적고 저렴합니다.

출처: https://ko.react.dev/learn/render-and-commit

const myUI = <div className="box">안녕</div>;

1️⃣ JSX -> React element: jsx로 작성한 코드를 번들러/컴파일러가 리액트 엘리먼트로 바꿉니다.

// 브라우저 메모리에 올라가는 실제 객체의 모습
const myUI = {
  type: 'div',
  props: {
    className: 'box',
    children: '안녕'
  }
};

2️⃣ React element -> Vitrual DOM(렌더 단계):  React Core 엔진(React Fiber)이 React Element를 React가 메모리에 유지하는 Fiber 노드 트리인 Virtual DOM으로 렌더링합니다. (createRoot().render() 또는 useState 변경 시) 리액트 앤진은 만들어진 엘리먼트들을 잔뜩 모아 가상 DOM(Virtual DOM)이라는 거대한 트리 구조로 조립합니다. 이전에 만들어둔 트리가 있다면 diffing을 수행하여 무엇이 바뀐지 지시서를 만듭니다.

-> Fiber란 React 16부터 도입된 내부 작업 단위로, 엘리먼트 하나당 fiber 노드가 하나가 대응되며, 상태,이펙트, 우선순위 등을 보유합니다. 개발자가 직접 다루지는 않고 React 내부에서만 사용됩니다.

-> Virtual DOM: Fiber 노드는 각 엘리먼트마다 일대일로 생성하며, 총 두개의 트리를 유지하고 있습니다. 하나는 현재 화면에 반영된 트리인 current이고 나머지는 업데이트 계산 중인 새 트리인 workInProgress입니다. 두 트리를 비교하는 알고리즘을 Reconciliation이라고 합니다. 가상 돔을 사용하는 이유는 변경 사항을 메모리(fiber 트리)에서 모두 계산하여 실제 dom 조작을 최소화하여, 레이아웃 재계산(reflow), 재출력(repaint) 횟수를 최소화합니다.

-> Reconciliation이란 이전 Fiber 트리와 새 엘리먼트 트리를 비교하여 변경된 노드를 찾는 Diffing 알고리즘으로, 크게 3가지의 규칙이 있습니다. 첫 번째 규칙은 type이 다르면, 서브트리 전체를 교체합니다. 이전에 Counter라는 컴포넌트를 <div>로 감싸다가 <span>으로 감쌌다면 내부에 있는 자식이 같아도 부모의 type 즉, 태그값이 변경되었기 때문에 변경합니다. 두 번째 규칙은 만약 type이 같다면, props만 업데이트합니다. 속성 값이 classname이 "old"->"new"로 변경되었다면, DOM 노드는 유지한채 내부값만 업데이트합니다. 세 번째 규칙은 리스트에서 엘리먼트를 렌더링할 때 key값을 사용하지 않으면 자식 노드르 전부 수정해야하는 비효율적인 상황이 발생합니다. 자식 엘리먼트에 데이터에 key를 부여하여 항목추가로 인한 DOM 조작 횟수를 최소화합니다.

-> 엘리먼트는 불변 객체로 만약, 상태 변경으로 인해 컴포넌트가 재호출되면, 기존 엘리먼트를 수정하는 것이 아니라 새로운 상태가 바녕된 완전히 새로운 엘리먼트 객체를 만들어냅니다.

 

3️⃣ Virtual DOM -> DOM element(커밋 단계): React DOM(react-dom 패키지)이 위에서 작성된 지시서를 바탕으로 순수 자바스크립트(DOM API)를 사용해 실제 브라우저 화면 요소인 DOM element를 만들어내거나 수정합니다.

// 1단계의 객체(myUI) 정보를 바탕으로 '진짜 DOM Element'를 생성
const realDomElement = document.createElement('div');
realDomElement.className = 'box';
realDomElement.textContent = '안녕';

// 브라우저 화면(root)에 붙임
document.getElementById('root').appendChild(realDomElement);

 

- Root DOM node은 리액트 애플리케이션이 실제 브라우저 DOM에 처음으로 접하는 단 하나의 진짜 HTML 태그 부분을 의미하며, 가상 돔과 현실 돔의 연결 통로입니다.

 

- setInterval(...)을 통해 1초마다 렌더링을 하여 세계시계 실습

import React from "react";

function Clock(props){
    return (
        <div>
            <h1>
                {props.timeZone} 현재시간: { new Date().toLocaleTimeString('ko',{timeZone: props.timeZone}) } 
            </h1>
        </div>
    )
}

export default Clock;
import { StrictMode } from 'react' //strictMode를 꺼냄
import { createRoot } from 'react-dom/client' //react-dom은 React를 브라우저 DOM에 그려주는 패키지
//import './index.css'
//import App from './App.jsx'
import Library from "./components/Library";
import Clock from './components/Clock';

const root = createRoot(document.getElementById("root"));

setInterval(()=> {
  root.render(
    <StrictMode>
      <Clock timeZone="Asia/Seoul"/>
      <Clock timeZone="Europe/Paris"/>
      <Clock timeZone="America/New_York"/>
    </StrictMode>,
  );
}, 1000);

-> 세계 시간을 나타내기 위해서 1초마다 렌더링됩니다. 이후에 useState 변수를 통해 더 효율적으로 변경할 수 있습니다.

728x90