서녕이네 개발단지

[React] Rendering (렌더링 동작원리)(1) 본문

프론트엔드/React

[React] Rendering (렌더링 동작원리)(1)

zero2-pooh 2023. 6. 10. 02:29

📄목차

1. 렌더링이란?

1-1. 렌더링 규칙

 

2. 렌더링 과정(동작 원리)

2-1. DOM에 element 렌더링 하기

2-2.  React Element

 

3. 재조정 (Reconciliation)

3-1. Diffing Algorithm(비교 알고리즘)

 


 

1. 렌더링(rendering)이란?

- 현재 props 및 상태를 기반으로 리액트가 컴포넌트에게 UI 영역이 어떻게 보이길 원하는지 설명을 요청하는 프로세스이다. 

💡간단히 말해, 코드로 정의된 내용이 실제 브라우저 화면에 그려지는 과정을 의미한다. 
리액트는 이때 컴포넌트의 props와 state를 기반으로 UI를 만든다.

 

❗️ 주의사항 ❗️

1. props의 변화로 인해 렌더링이 이루어지는 것이 아니다.

2. 렌더링이 이루어진 후 props의 변화를 가지고 화면을 그린다.

 

따라서!! 👉🏻 렌더링 추적 및 최적화는 철저히 state기반으로 이루어져야 한다.  

 

 


1-1. 렌더링 규칙

리액트 렌더링의 중요한 규칙 중 하나는 렌더링은 '순수' 해야 하고 '부수작용'이 없어야 한다는 것이다.

근데 이는 매우 복잡하고 어려운데, 왜냐하면 대다수의 부수 작용이 왜 일어났는지 뚜렷하지 못하고, 어떤 것도 망가 뜨리지 않기 때문이다.

 

예를 들어, 엄밀히 말하면 console.log()도 부수작업을 야기하지만, 그 어떤 것도 망가 뜨리지 않는다.

prop이 변경되는 것은 명백한 부수효과 이며, 이는 무언가를 망가 뜨릴 수 있다.

렌더링 중간에 ajax 호출 또한 부수효과를 일으키고, 이는 요청의 종류에 따라서 명백하게 앱에 예기치 못한 결과를 야기할 수 있다.

 

렌더링을 포함한 다양한 리액트의 라이프 사이클 메서드의 동작과, 어떠한 동작이 '순수' 한지, 혹은 안전한 지를 나타내고 있다.

요약하자면 렌더링 로직이 할 수 없는 것!

 

  • 존재하는 변수나 객체를 변경해서는 안된다.
  • Math.random() Date.now()와 같은 랜덤 값을 생성할 수 없다.
  • 네트워크 요청을 할 수 없다.
  • state를 업데이트

 

렌더링 로직은

  • 렌더링 도중에 새롭게 만들어진 객체를 변경
  • 에러 던지기
  • 아직 만들어지지 않은 데이터를 lazy 초기화하는 일 (캐시 같은)

등이 가능하다.


 

2. 렌더링 과정 (동작 원리)

2-1. DOM에 element 렌더링 하기
<div id="root"></div>

이렇게 안에 들어가는 모든 element를 React DOM에서 관리하기 때문에 이것을 '루트(root)' DOM 노드라고 한다.

 

const root = ReactDOM.createRoot(
  document.getElementById('root')
);
const element = <h1>Hello, world</h1>;
root.render(element);

위 코드와 같이 기본적으로 리액트의 렌더링은

우선 DOM 엘리먼트를 ReactDOM.createRoot()에 전달한 다음 ➡️ React Element를 root.render()에 전달하는 과정을 거친다.

 

👉🏻 코드를 실행하면 화면에 "Hello, world"가 보일 것이다. 


+ 설명을 더 하자면~

createElement()를 사용하여 React Element를 만들었다.

React Element를 조합(➕)해서 리액트 앱을 만들게 된다. (컴포넌트는 최소한의 빌딩 블락인 리액트 엘리먼트 몇개로 구성된 UI 조각이다).

 


 

2-2. React Element

 

React Element란? 

- React Element는 쉽게 말해 ~ 리액트 앱을 구성하는 최소한의 빌딩 블락이다.

- 리액트 엘리먼트는 typeprops를 가지는 React 만의 객체이다.  

- React.createElement()를 이용해 만들 수 있다. 

- type 으로 HTML 태그 이름을 가지고, 그 이외의 특징을 props로 관리하는 객체형태로 정의된다. 

🖐🏻 간략한 추가 설명
- 이 React Element는 자바스크립트 객체이며 이것을 DOM으로 렌더링 해주는 역할은 ReactDOM 이 수행한다.
즉, ReactDOM.render()를 통해서 root 엘리먼트에 DOM을 렌더링 하게 된다!!

 

❗️참고해야 할 점❗️

- React 엘리먼트는 불변객체다.

- 엘리먼트를 생성한 이후에는 해당 엘리먼트의 자식이나 속성을 변경할 수 없다❌.

- 엘리먼트는 영화에서 하나의 프레임과 같이 특정 시점의 UI를 보여준다.

 

~~ 지금까지 소개한 내용을 바탕으로 하면 UI를 업데이트하는 유일한 방법은? 

=> 새로운 엘리먼트를 생성하고 이를 root.render()로 전달하는 것이다.

 

⚠️주의⚠️
실제로 대부분의 React 앱은 root.render()를 한 번만 호출한다.

3. 재조정 (Reconciliation)

🙌 리액트에 있어서 재조정(Reconciliation)의 개념을 이해하려면 Virtual DOM의 이해가 필요하다. (전 포스팅에 가상돔 설명 참조.)

 

❓재조정이란? 

- 리액트는 선언적 API를 제공하기 때문에 리액트 사용자는 렌더링 작업을 함에 있어서 매번 무엇이 바뀌었는지걱정할 필요가 없다. 

- 하지만!!  내부에서는 기존의 가상 돔과 변경사항이 생긴 가상 돔의 비교작업이 이루어지는데

👉🏻이 과정을 Reconciliation(재조정) 이라고 말한다.

 

이 과정을 거치려면, 실제로는 모든 DOM 트리를 순회하면서 탐색 및 변경하는 과정을 거쳐야 한다.

하지만 이런 모든 과정을 거친다면 최첨단의 알고리즘을 이용해도 O(n^3)의 시간복잡도를 가진다고 한다.🥲

 

따라서! 리액트는 조금은 다른 휴리스틱한 알고리즘을 이용해, 시간복잡도를 O(n)까지 줄이는 방식을 택한다.

 

참고❗️ 
v16이상 부터는 React Fiber 라는 새로운  알고리즘을 적용해 기존의 Reconciliation 알고리즘에서 발생하는 애니메이팅에 대한 부분을 개선했다고 한다.

 

 

브라우저에 렌더링을 할 때 어떻게 기존의 컴포넌트와 변경이 되었는지 확인하기 위해 리액트에서 

* Diffing Algorithm(비교 알고리즘)을 사용하여  이 알고리즘을 통해 컴포넌트의 갱신한다.

 

리액트는 아래의 2가지 가정을 기반으로 O(n)의 시간복잡도를 가지는 휴리스틱 알고리즘을 구현했다.

  1. 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어 낸다.
  2. 개발자가 key prop을 통해, 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할지 표시해 줄 수 있다.

 


3-1. Diffing Algorithm(비교 알고리즘)

 

- 두 개의 트리를 비교할 때, React는 두 엘리먼트의 루트(root) 엘리먼트부터 비교한다.

이후의 동작은 루트 엘리먼트의 타입에 따라 달라진다.

 

 

DOM 엘리먼트의 타입이 다른 경우

<div>
  <Counter />
</div>

<span>
  <Counter />
</span>

 

👉🏻 이전 트리를 버리고 완전히 새로운 트리를 구축 (비효율적이다)

 

 

DOM 엘리먼트의 타입이 같은 경우

<div className="before" title="stuff" />

<div className="after" title="stuff" />
// 변경된 속성들에 대해서만 갱신


<div style={{color: 'red', fontWeight: 'bold'}} />

<div style={{color: 'green', fontWeight: 'bold'}} />
// 스타일 또한, 변경 사항이 있는 속성만 업데이트 (color 속성만 업데이트함)

 

 

 

참고

https://narup.tistory.com/272

https://www.nextree.io/riaegteu-rendeoring-mic-coejeoghwa/
https://eastflag.co.kr/react/script-tag/react-element/

https://ko.legacy.reactjs.org/docs/rendering-elements.html#gatsby-focus-wrapper

https://programming119.tistory.com/240

https://yceffort.kr/2022/04/deep-dive-in-react-rendering#리액트-렌더링-규칙