React의 렌더링을 우선적으로 이해하기 위해서는 DOM을 이해해야 한다.
DOM을 이해하려면 브라우저 렌더링 과정에 대해 이해하고 있어야 하는데, 이 포스트는 react의 렌더링 과정에 대한 포스트니까 간단한 설명만 하고 넘어가도록 하겠다.
예를 들어 사용자가 구글 창에 어떤 url 주소를 입력한 경우, 브라우저는 해당 서버로 html 파일을 요청하게 된다. 서버로 부터 html 파일을 받아오면 파싱을 통해 DOM이 생성된다. 이 DOM은 문서 객체 모델 DOM(Document Object Model)을 의미한다.
문서 객체란 html, head, body와 같은 태그들을 javascript가 이용할 수 있는 (메모리에 보관할 수 있는) 객체를 의미한다.
즉, 돔은 웹 페이지를 이루는 태그들을 자바스크립트가 이용할 수 있게끔 브라우저가 트리구조로 만든 객체 모델을 의미한다.
DOM은 HTML과 스크립팅언어(Javascript)를 서로 이어주는 역할을 하고 있는 것이다.
색상을 바꾸거나 순서를 바꾸거나 수정을 할 때 document라는 전역 객체를 통해 접근할 수 있다.
시간 복잡도는 O(n^3)를 갖는다.
이 DOM과 스타일시트로 부터 불러오는 css를 파싱한 CSSOM으로 렌더트리를 생성하게 되고, 이 렌더트리를 활용한 레이아웃과 페인트 과정을 거쳐서 브라우저에 최종적으로 화면을 띄워주게 된다.
React의 경우 실제 돔에 접근하여 조작하는 대신, 이것을 추상화시킨 자바스크립트 객체를 이용해 접근 할 수 있는 DOM의 사본인 virtual dom(가상돔)으로 렌더링을 해준다.
React 렌더링 조건
에 따라 변화가 일어나면 렌더링이 시작된다. flag가 있는 컴포넌트와 자식 컴포넌트가 파싱된다.
이렇게 파싱된 react element는 메모리에 저장이 된다.
메모리에 저장된 react element로 가상돔을 생성한다.
변화 전의 가상돔과 변화된(생성한) 가상돔을 비교한다.
비교 알고리즘
에 따라 변화의 범위가 결정된다. 메모리 누수와 속도
매번 h1에 관련된 접근 메서드를 사용한다면 매단계마다 저 수많은 document 객체들을 전부 훑으며 찾는 현상이 발생되고 이것은 곧 메모리 누수로 이어진다. 또한 h1의 변화가 일어난다면 (DOM의 변화가 일어난다면) css를 다시 연산하고 레이아웃을 구성하고 웹페이지를 다시 그려주는 데에서도 시간이 든다.
코드량
다른 하나는 객체를 찾기 위해 작성하는 코드가 번잡스러울 수 있다. id라는 고유성을 침해 당하지 않기 위해 해당 태그의 네이밍을 정할 때 심사숙고해야 할 것이고 해당 태그를 접근하기 위해 작성해야 하는 메서드가 그리 짧지가 않다. (document.getElementById('h1의 id'))
React가 변경 사항으로 재렌더링을 수행하는 조건은 다음과 같다.
- Props가 변경되었을 때
- State가 변경되었을 때
- forceUpdate() 를 실행하였을 때
- 부모 컴포넌트가 렌더링되었을 때
재렌더링이 일어나면서 이전의 가상돔과 새로운 가상돔을 비교할 때 적용되는 비교 알고리즘이다.
이 알고리즘 적용으로 시간복잡도를 O(n)까지 줄이게 되었다.
위와 같은 React의 비교 알고리즘을 적용하여 시간복잡도를 줄였음에도 불구하고 컴포넌트가 렌더링하는 엘리먼트가 수천 수만 개라면? O(n)도 느리다.
하지만 컴포넌트에서 렌더링 결과에 전혀 영향을 미치지 않는 변경사항이 발생하게 된다면, 불필요한 렌더링이 발생 하므로 성능 손실이 발생한다. 이는 렌더링에서 수행하는 로직이 많을 수록, 많은 컴포넌트를 출력할 수록 손실은 배가 된다.
const Student = React.memo(() => <div>hello</div>)