teklog

Learning React 1

2023/08/01

n°41

category : React

리액트에 대해 더 깊게 알고 싶어져 Learning React를 읽었다. 빠르게 완독하고, 리액트 문서를 읽은 뒤 다시 궁금했던 부분을 살펴보았는데, 실용적인 설명 위주인 문서보다 조금은 더 깊게 설명하는 부분이 있어 메모한다. 문서와 이 책을 번갈아보면 이해가 깊어져 좋다. 대부분의 서적이 훅의 사용법 자체에만 집중하고 있기 때문에, 약간은 더 깊게 살펴보기에 적절했다. (사실은 더 깊게 알고 싶지만, 이 책 역시 필요한 개념을 전달하기 위해 예제 중심으로 작성된 건 마찬가지다.)


이번 글도 마찬가지로 책에서 중요하다 싶은 부분의 발췌와 노트로 이어진다. 리액트의 가장 근간인 개념을 간략히 살펴보고자 한다.


React란?


리액트는 뷰(UI)를 만들기 위한 라이브러리
ReactDOM은 브라우저에 렌더링하기 위한 라이브러리


리액트는 브라우저 DOM을 갱신해주기 위해 만들어진 라이브러리다. 리액트가 모든 처리를 대신 해주기 때문에 더 이상 SPA를 더 효율적으로 만들기 위해 여러 복잡한 내용을 신경쓸 필요가 없다. 리액트에서는 코드로 DOM API를 직접 조작하지 않는다. 대신 리액트에게 어떤 UI를 생성할지 지시하면, 리액트가 우리 명령에 맞춰 원소 렌더링을 조절해준다.



리액트 엘리먼트 React Element


가상 DOM은 리액트 엘리먼트로 이루어진다. 리액트 엘리먼트는 실제 DOM 엘리먼트가 어떻게 생겨야 하는지를 기술한다. 즉 리액트 엘리먼트는 브라우저 DOM을 만드는 방법을 알려주는 명령이다. DOM을 렌더링하기 위한 설계도, DNA 같은 것으로 이해된다. 즉 리액트에서 작성한 JSX 컴포넌트는 사실 리액트 엘리먼트의 객체를 만드는 과정인 것이다.


리액트 엘리먼트는 이렇게 생겼다. 


{
 "$$typeof": Symbol(react.element),
 "type": “h2,
  "key": null,
  "ref": null,
  "props": {
      "id": 'recipe-0', "children": "딸기 케이크"
     },
  “_owner”: null,
  “_store”: {}
}



  • $$typeof: React 내부에서 React 엘리먼트를 식별하는 데 사용됨
  • type : 만드려는 HTML, SVG 앨리먼트의 타입을 지정 (h2, div, etc..)
  • props: DOM 엘리먼트를 만들기 위한 데이터, 자식 엘리먼트를 표현
  • key: 옵셔널. 리액트가 동적인 리스트들의 컴포넌트를 렌더링할 때 각 컴포넌트를 구별하는 것을 도와주기 위해서 필요. 유니크한 값을 사용하여 성능개선을 이룰 수 있다
  • ref: 옵셔널. 컴포넌트가 렌더링될 때 기존 DOM 노드 또는 React 컴포넌트 인스턴스에 대한 참조를 얻기 위해 사용. 주로 직접 DOM 조작을 하거나 (useRef), React 컴포넌트와 명령형으로 상호 작용하는 데 사용(? 이부분은 이해되지 않는다.).
  • *_owner: _owner 프로퍼티는 현재 React 엘리먼트를 생성한 (또는 "소유한") React 컴포넌트를 내부적으로 참조.컴포넌트 또는 중첩된 엘리먼트를 다룰 때 React 컴포넌트 트리 내의 소유 관계를 추적하는 데 사용. 이를 통해 React는 특정 엘리먼트를 생성한 컴포넌트를 식별 가능
  • *_store: _store 프로퍼티는 React 엘리먼트와 관련된 내부 데이터를 보유하는 데 사용. 또한 React에 의해 최적화와 내부적인 기타 작업을 위해 사용됨. _store 프로퍼티에 저장된 구체적인 데이터는 React 버전과 내부 구현 세부사항에 따라 다를 수 있음.


*owner와 store는 애플리케이션 코드에서 직접 사용하는 것을 권장하지 않는다고 한다.


후에 살펴볼 예정이지만, 이 리액트 엘리먼트는 ReactDOM.render()함수에 들어가서 DOM 렌더링이 일어난다. 브라우저의 DOM이 DOM 엘리먼트로 이뤄지는 것처럼, 가상 DOM은 리액트 엘리먼트로 이루어 진다. 리액트 엘리먼트는 개념상 HTML 엘리먼트와 비슷하지만 실제로는 자바스크립트 객체다. 리액트 엘리먼트는 실제 DOM 엘리먼트는 DOM API를 직접 다루는 것보다 자바스크립트 객체인 가상 DOM을 직접 다루는 편이 훨씬 더 빠르다. 우리가 가상 DOM을 변경하면 리 액트는 DOM API를 통해 그 변경 사항을 가장 효율적으로 렌더링해준다. DOM 엘리먼트와 리액트 엘리먼트의 겉모습은 비슷해 보일지 몰라도 그 실체는 꽤 다르다. 리액트 엘리먼트는 그에 대응하는 실제 DOM 엘리먼트가 어떻게 생겨야 하는지를 기술한다. 다른식으로 말하자면 리액트 엘리먼트는 브라우저 DOM을 만드는 방법을 알려 주는 명령이다.


리액트 엘리먼트를 만들기


Var dish = React.createElement(“h2”, {id: “recipe-0”}, “딸기 케이크”, React.createElement(...))


첫번째 인자는 엘리먼트의 태그 타입 (h2) 

두번째 인자는 태그의 id

세번째 인자는 태그 사이에 들어가야 할 자식 노드

4번째 인자는 children. 옵셔널이다. 그 안에서 createElement로 선언된 엘리먼트는 바깥 함수의 자식 컴포넌트가 된다.


-> 렌더링 과정에서 리액트 엘리먼트가 실제 DOM으로 변한다.



리액트 돔 React DOM


리엑트 돔에 리액트 엘리먼트를 브라우저에 렌더링하는데 필요한 도구가 render() 메서드가 들어있다.


ReactDOM.render(dish, document.getElementById(‘root’))

위에서 선언한 리액트 엘리먼트 dish를 render 메서드 첫번째 인자로, 타겟 엘리먼트를 DOM에서 id로 불러온다. Render 함수는 배열 또한 렌더링할 수 있다. 첫번째 인자로 [dish, drink]를 넣을 수 있다. 배열의 인자로 들어가면, 두 리엑트 엘리먼트는 형제 노드로 렌더링된다.



하지만 실제로 리액트를 사용하는데 있어 React.createElement나 ReactDOM.render를 사용할 일은 없다. 모든 컴포넌트는 결국 JSX를 사용하여 작성하게 되는데, 이 문법은 리액트가 처음 나왔을 때부터 공개되었다고 한다. 아마 HTML 코드와 자바스크립트의 유연한 경계에서 각광을 받았을듯 싶다. 이 JSX는 바벨을 통해 자바스크립트 객체로 변환된다. 이 객체(리액트 엘리먼트)는 이후 리액트 돔의 render의 인자로 들어가 렌더링으로 발생한다.



function Greeting(props) {
 return <h1>Hello, {props.name}!</h1>;
}

// Step 2: Babel Transformation (JSX to React.createElement)
function Greeting(props) {
 return React.createElement("h1", null, "Hello, ", props.name, "!");
}

// Step 3: React Element
const element = React.createElement(Greeting, { name: "John" });

// Step 4: Rendering with ReactDOM
const container = document.getElementById("root");
ReactDOM.render(element, container);