[React]렌더링에 대한 고찰

2023. 1. 19. 10:23programing/react

반응형

1. 개요

React를 공부하면서 어떻게 보면 가장 기초이고, 가장 중요한 부분인데 사실 이제야 포스팅한다는 것은 이제까지 리액트를 잘못 사용해왔다는 반증이기도 한데... 조금 더 심도있게 리액트를 알아보기 위해서 렌더링이란 무엇이고, 렌더링의 조건이 어떻게 되는 건지 확인차 적어보려고 합니다.

 

 

 

2. 렌더링이란?

아래의 Welcome을 컴포넌트라고 부릅니다. (정확히는 함수 컴포넌트)

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

리액트에서 렌더링이란 컴포넌트가 현재 props와 state의 상태에 기초해 UI를 어떻게 구성할지, 컴포넌트에게 작업을 요청하는 것을 의미합니다.

3. 렌더링 과정

3-1. React의 렌더링

기본적인 리액트의 렌더링 개념은 루트(<div id="root"></div>) DOM부터 시작해 플래그가 지정되어 있는 모든 컴포넌트를 찾아서 렌더링을 진행하는 것을 말합니다.

* root id는 public/index.html에 기본적으로 설정이 되어있습니다.

* DOM은 HTML parser에 의해 생성된 트리 구조의 Node 객체 모델입니다.

 

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

기본적으로 리액트의 렌더링은 이러한 DOM 엘리먼트를 ReactDOM.createRoot()에 전달한 다음, 그 React Element를 root.render()에 전달하는 과정을 거칩니다.

 

 

 

 

3-2. React Element

자, 그럼 React Element는 무엇일까요? 리액트 엘리먼트는 type props를 가지는 React만의 객체입니다. React.creatElement()를 이용해 만들 수 있으며, type으로 HTML 태그 이름을 가지고, 그 이외의 특징을 props로 관리하는 객체 형태로 정의됩니다.

// createElement를 이용해서 React Element 만들기
React.createElement(
  'div',
  { className: 'name' },
  'React'
)
// 위와 같은 의미 (JSX 문법)
<div className='name'>React</div>


// createElement를 이용해서 만들어진 React Element 객체
{
  type: 'div',
  props: {
    className: 'name',
    children: 'React'
  }
}

사실 위와 같은 createElement()라는 함수를 사용하기가 어렵기도 하고, 직관적이지도 않습니다.

따라서 JSX 문법을 사용해 보통 위의 객체를 DOM 형태(<div className='name'>React</div>)로 정의하여 사용합니다.

 

이러한 리액트 엘리먼트는 불변객체로 작용해서, 엘리먼트가 생성된 이후에는 해당 엘리먼트의 속성과 자식을 변경할 수 없다는 특징을 가지게 됩니다.

 

 

 

3-2. Virtual DOM

리액트의 특징 중 하나의 가상 돔(Virtual DOM)은 실제 DOM의 구조와 비슷한 React 객체의 트리를 말합니다.

웹 브라우저에서 사용자는 여러 가지 액션을 통해 웹 서버에 요청하고, 그에 따른 응답으로 DOM 구조가 빈번하게 바뀌는데, 이때마다 DOM이 수정되어 Render Tree가 생성되고, Reflow, Repaint의 과정을 계속 수행하게 된다면 굉장히 답답한 상황이 발생하게 될 것입니다.

이때 React에서는 Virtual DOM을 실제 DOM에 필요한 부분만 적절하게 반영해서 불필요한 수정이 일어나지 않도록 해주는데, 이 Virtual DOM의 가장 큰 장점은 개발자가 직접 DOM을 조작하지 않아도 된다는 점이고, 이러한 과정을 모두 자동화해준다는 점입니다.

또한 DOM의 수정을 batch로 한 번에 처리하기 때문에 리렌더링 연산을 최소화 할 수 있습니다.

 

 

 

 

3-3. 재조정(Reconciliation)

앞서 설명한 Virtual DOM에서 어떻게 리액트는 실제 DOM과 가상 DOM을 구별하는지 궁금할 텐데, 결과적으로 브라우저에 렌더링을 할 때 어떻게 기존의 컴포넌트와 변경이 되었는지 확인하기 위해 리액트에서 diffing 알고리즘을 사용하여  이 알고리즘을 통해 컴포넌트의 갱신합니다.

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

 

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

위의 재조정 단계를 거쳐 이전 elements와 새로 생성된 elements를 비교해 엘리먼트가 변경되었다면 렌더링을 수행합니다.

 

 

 

 

 

4. 리렌더링

앞에서 렌더링을 설명했는데, 리액트에선 초기에 한번 렌더링을 진행하고, 그 이후에 특정 조건이 발생하면 다시 렌더링을 진행하는 리렌더링이라고 하는 것이 있습니다.

  • 내부 상태(state) 변경시
  • 부모에게 전달받은 값(props) 변경시
  • 중앙 상태값(Context value 혹은 redux store) 변경시
  • 부모 컴포넌트가 리렌더링 되는 경우

위의 경우가 컴포넌트가 리렌더링 되는 조건입니다.

 

리액트가 아무리 최적화가 잘 되어있다고해도, 무분별하게 렌더링이 일어날 경우 성능 저하가 일어나게 되기 때문에, 이러한 조건들을 기준을 두고 코드를 작성하여 무분별하게 렌더링이 일어나지 않도록 주의하여야 합니다.

* redux store 변경시 자동으로 리렌더링이 되는 이유는, 리덕스 스토어가 <Provider store={store}>로 컴포넌트를 감싸주었을 때, 스토어 상태가 변경될 때마다 이를 참조하는 컴포넌트들이 리렌더링이 될 수 있도록 react-redux 라이브러리가 자동적으로 컴포넌트 들의 렌더 함수들을 subscribe 해주기 때문입니다.

 

 

4-1. 리렌더링 과정

  1. 위의 조건을 통해 컴포넌트 리렌더링
  2. 구현부 실행 = props 취득, hook 실행, 내부 변수 및 함수 재 생성
  3. return 실행, 렌더링 시작
  4. 렌더 단계(Render Phase): 새로운 가상 DOM 생성 후 이전 가상 DOM과 비교해 달라진 부분을 탐색하고 실제 DOM에 반영할 부분을 결정
  5. 커밋 단계(Commit Phase): 달라진 부분만 실제 DOM에 반영
  6. useLayoutEffect: 브라우저가 화면에 Paint하기 전에 useLayoutEffect에 등록해둔 effect(부수 효과 함수)가 동기적으로 실행되며, 이때 state, redux store 등의 변경이 있다면 한번 더 리렌더링
  7. Paint: 브라우저가 실제 DOM을 화면에 그림. didUpdate 완료.
  8. useEffect: update되어 화면에 그려진 직후, useEffect에 등록해둔 effect(부수 효과 함수)가 비동기로 실행

 

[출처]https://narup.tistory.com/272

 

[React] 렌더링(리렌더링)

1. 개요 React를 공부하면서 어떻게 보면 가장 기초이고, 가장 중요한 부분인데 사실 이제야 포스팅한다는 것은 이제까지 리액트를 잘못 사용해왔다는 반증이기도 한데... 조금 더 심도있게 리액

narup.tistory.com

 

반응형