리액트에서 렌더링이라는 단어가 굉장히 많이 혼용되고 있다.
보통 Virtual DOM, 어떠한 상태가 변경되었을 때 리액트의 컴포넌트 트리가 변경되는 과정
더하기 UI가 그려지는 과정까지를 일반적으로 렌더링이라고 칭하곤 한다.
Fiber에서 컴포넌트 재조정까지는 확실하게 수행을 하고 그리고 렌더링까지도 수행을 할텐데
그런데 여기서, 렌더링 실행 결과가 무엇이냐에 따라서 사용하게되는 렌더러가 바뀌게 된다.
이를 좀 더 자세하게 생각해보면 다음과 같을 것이다.
컴포넌트 재조정 = 렌더 함수를 실행할 지 결정
렌더링 = 실행된 이전 <> 현재 렌더 함수의 결과물인 React Element Tree 비교 후
실제 UI 변경이 필요한 경우
플랫폼에 맞는 UI 그리는 API 실행 (DOM / Obj-C / Java / .net ...)
컴포넌트 재조정과 렌더링이 다른 절차, 각각 나위어져있는 절차로 실행되게 된다.
그런데 리액트 15.6에서 React 코어와 ReactDOM이라는 라이브러리가 따로 분리되게 된다.
이는 ReactDOM이라는 것은 결국 리액트의 컴포넌트 재조정 과정과 실제로 재조정된 컴포넌트를 UI에 그리는 과정, 즉 렌더링하게되는 과정이 분리되었다고 볼 수 있다.
그렇다면 이제는 리액트가 DOM 뿐만 아니라 어떠한 것이라도 렌더링 할 수 있다는 말이 된다.
결국 리액트의 컨셉만 가지고 굉장히 많은 것들을 개발할 수 있게 되었다.
그리고 아마 그 중 가장 대표적인 것이 React Native가 될 것이다.
React Native는 렌더러가 브릿지를 통해서 네이티브 API(Object-c나 Object-c의 UI API들 아니면 자바 안드로이드 xml변환 등) 등 여러가지를 예상해 볼 수 있겠다.
결국 리액트 코어의 컴포넌트 재조정 과정은 리액트가 가지고 있고 컴포넌트 재조정 결과를
DOM이나 React Native, 심지어 React Native로 작성한 컴포넌트를 웹으로 변환하는 렌더러도 있고
네이티브로 작성한 컴포넌트를 윈도우UI나 MacOs UI로 변환해주는 렌더러 또한 존재한다.
3D 렌더링을 할때 사용하는 리액트 3 Fiber라는 라이브러리도 있다. 이는 thressJs 등을 함께 비교하는 알고리즘을 이용하여 Fiber라는 컴포넌트 재조정 렌더링 라이브러리에다가 구현을 해놨다는 것. 등등 머리가 아파진다.
여기서 가장 중요한 부분은 컴포넌트 재조정과 렌더링이 다른 절차, 각각 나뉘어져 있는 절차로 실행된다는 점, Virtual DOM 비교와 실제 렌더링 과정은 분리되어 있고 렌더링 라이브러리 역시 분리된 코드베이스를 가지고 있다.
즉, Virtual DOM 비교와 실제 렌더링 과정은 분리되어 있다.
=> 컴포넌트의 렌더 함수를 실행하더라도 UI는 바뀌지 않을 수 있다.
리엑트는 Virtual Dom을 사용해서 보다 효율적으로 우리가 원하는 페이지를 빠르게 브라우저의 그려 준다.
우선 Virtual Dom 전에, DOM은 무엇인지부터 생각해보자.
DOM이란?
DOM은 Document Object Model.
우리가 브라우저를 통해서 어떤 웹사이트에 들어가면
제목도 있고 사진도 있고 버튼도 있다.
이런 하나하나의 요소들을 엘리먼트 라고 부르며
이 모든 것을 담고 있는 웹페이지를 문서, Document 라고 한다.
브라우저는 이 페이지에 해당하는 html 을 분석해서 화면에 띄워준다.
그래서 DOM 이란 웹 페이지에 들어가 있는 html 엘리먼트들을 트리 형태의 구조로 표현한 것.
돔 트리 안에는 각각의 엘리먼트의 사용하는 노드가 들어있다.
개발자들은 DOM이 제공하는 api 를 통해서
돔 구조의 접근을 하고 원하는 엘리먼트의 구조 내용 스타일 등을
입맛대로 변경시킬 수 있다.
또한 이러한 행위를 돔 조작 이라고 한다.
만약 플레인 자바스크립트를 사용한다면 getElementById나 querySelector 같은
api를 통해서 DOM 구조 안에 있는 엘리먼트에 접근해서 원하는 대로 내용 스타일의 레이아웃 등을 수정할 수 있다.
Virtual DOM 이란?
Virtual DOM 이라는 것은 대체 무엇일까?
리액트 는 Virtual DOM을 사용해서 실제 DOM을 조작하는 일을 훨씬 빠르게 만들어 준다.
가상 돔이라는 건 실제 돔과 같은 내용을 담고 있는 복사본 이라고 생각하면 편하며, 이 복사본은 실제 돔이 아닌 자바스크립트 객체 형태로 메모리 안에 저장되어 있다.
가상 돔은 실제 돔의 복사본이기 때문에 실제 돔 안에 있는 모든 엘리먼트 들과 속성들을 똑같이 갖고 있다.
하지만 가상 돔은 실제 돔과는 다르게 브라우저에 있는 문서에 직접적으로 접근을 할 수 없다.
그렇기 때문에 당연히 화면에 보여지는 내용을 직접 수정할 수도 없다.
여기까지 들으면 가상 돔은 별로 쓸모가 없어 보일 수도 있다.
이미 존재하는 실제 돔을 가져다가 왜 굳이 복사본을 만들면서까지 따로 메모리에 저장을 해 놓는 걸까?
하지만 리엑트는 이 가상 돔을 굉장히 똑똑하게 사용해서 우리가 실제 돔을 조작하는
데 걸리는 시간을 획기적으로 줄여준다.
자 그럼 먼저 실제 돔 DOM 조작 과정 조작은 어떻게 이루어지는지 한번 알아보자.
웹페이지에 있어서 돔 조작은 없어서는 안될 필수적인 과정이다.
간단한 게시판 앱만 보아도 게시물을 추가하거나 댓글을 달 때 또는 좋아요 를 누르는 것처럼 아주 간단한 일을 할 때도 화면에 보여지는 내용을 바꾸기 위해서는 돔을 조작해야 한다.
돔의 내용을 수정해 줄 때마다 다음과 같은 과정이 수행된다.
브라우저가 현재 페이지의 html 을 쭉 탐색해서 해당이 엘리먼트를 찾고,
해당 엘리먼트와 자녀 엘리먼트 들을 돔에서 제거,
그리고 새롭게 수정된 엘리먼트 들로 교체,
그리고 나서 css 를 다시 계산하고 레이아웃 정보를 알맞게 수정하고
새롭게 계산된 내용에 따라서 브라우저의 다시 그린다.
되게 뭔가 복잡하다는 것을 느낄 수 있다.
그래도 돔은 트리 구조로 되어 있기 때문에
트리에 있는 정보를 업데이트 시켜주는 것이 그렇게 무거운 작업은 아니다
(빠른 알고리즘을 사용해서 트리를 효율적으로 업데이트 시켜 줄 수 있으니깐).
하지만 매번 돔을 조작할 때마다 브라우저 화면의 ui 를 새롭게 그려 주는 작업은 꽤나
복잡하고 시간이 걸리는 작업이다.
만약 이런 리스트 안에 5개의 항목들의 스타일을 하나하나 따로 변경해야 된다면 이 과정을 5번이나 반복해서 화면에 다시 그려져야 되는것이며 굉장히 비효율적이다.
가상돔을 이용한 DOM 조작 과정,
리액트는 가상 돔 이라는 것을 이용해서 이런 비효율적인 돔 조작을
훨씬 더 빠르게 만들어 준다.
가상 돔은 실제 돔과 똑같이 생긴 복사본인데,
실제 돔 안에 있는 모든 엘리먼트 들과 그 속성들을 동일하게 갖고 있다.
하지만 실제 돔 과는 다르게 직접적으로 화면에 보이는 ui 를 조작할 수 있게 해주는 api를 제공하지 않는다.
왜냐하면, 가상 돔은 메모리에 저장되어있는 자바 스크립트 객체일 뿐이니까.
하지만 그렇기 때문에 가상 돔을 생성하고 접근하는 것은 아주 가볍고 빠른 작업이다.
(실제 브라우저 화면에 접근하는 게 아니니까)
자 그렇다면 리액트 는 어떤 식으로 가상 돔을 활용해서 보다 더 효율적으로 실제 도움을 조작할 수 있게 해주는 걸까?
리액트는 항상 2개 가상 돔 객체를 갖고 있다. 또한 이를 더블 버퍼링이라고 부른다.
첫번째 가상 돔은 이전의 화면 구조를 담은 객체이고,
두번째 가상 돔은 렌더링 이후에 보이게 될 화면 구조를 담은 객체이다.
리액트는 상태가 변경될 때마다 화면이 새로 렌더링이 된다.
리액트는 렌더링이 발생 될 상황에 놓일 때마다
실제 브라우저의 그려지기 이전에 새로운 화면에 들어갈 내용이 담긴 가상 돔을 생성하며
그 후에 렌더링 이전에 화면의 내용을 담고 있는 첫번째 가상 돔과 업데이트 이후의 내용을 담고 있는 두 번째 가상 돔을 비교해서 정확히 어느 엘리먼트 들이 변했는지 찾아낸다. 또한 이러한 과정을 리액트는 디핑(Diffing) 이라고 부른다.
디핑은 효율적인 알고리즘을 사용해서 진행되기 때문에 정확히 어느 엘리먼트 들이 변경되었는지 굉장히 빠르게 파악할 수 있다.
이렇게 바뀐 엘리먼트 들을 파악한 다음에 리엑트는 딱 그 부분만 딱 그 바뀐 부분들만 실제 돔에 적용시켜 준다.
즉, 딱 바뀐 엘리먼트 들만 찝어가지고 실제 브라우저 화면에 적용시키며며,
이를 바로 재조정(Reconciliation)이라고 부른다.
리액트의 재조정 과정이 굉장히 효율적인 이유는 바로 배치 업데이트 덕분이다.
배치는 집단 혹은 무리 라는 뜻이며,
배치 업데이트란 변경된 모든 엘리먼트 들을 집단으로, 실제 돔에 한번에 적용 시켜 주는
것을 뜻한다.
만약 리스트 안에 열 개의 항목이 바뀌었다면 돔을 10번 모두 조작하는 게 아니라,
한꺼번에 바뀐 모든 부분들을 집단으로 적용 시켜 주는 것이다.
아까 말했듯이 돔 조작에 있어서 가장 비용이 많이 드는 작업은 화면을 그려주는 작업이다.
배치 업데이트는 변경된 엘리먼트를 하나하나 따로따로 반복해서 그려주는게 아니라 변경된 내용을 한번에 다 받아와서 실제 돔의 한꺼번에 적용 시켜 주는 것이기 때문에 훨씬 더 빠르고 효율적인 것이다.
정리
1. 리액트의 가상 돔은 실제 DOM과 같은 내용을 담고 있는 복사본이다. 그리고 이 복사본은 자바스크립트 객체 형태로 메모리상에 저장되어있다.
2. 리액트 는 항상 두 개의 가상 돔을 갖고 있다. 첫 번째 가상 돔은 변경 이전의 내용을 담고 있고 두번째 가상 돔은 변경 이후에 보여줄 내용을 담고 있다.
3.변경된 내용이 화면에 새롭게 그려지기 이전, 즉 실제 돔이 변경 되기 이전에, 리액트는 두 개의 가상 돔을 비교해서 정확히 어떤 엘리먼트들이 바뀌었는지 효율적으로 파악한다. 그리고 이 과정을 Diffing이라고 부른다.
4. Diffing을 통해서 바뀐 부분들을 파악한 이후, 리액트는 배치 업데이트를 수행함으로써 변경된 모든 엘리먼트 들을 실제 돔에 한꺼번에 적용시킬 증가 그리고 이러한 과정을 Reconsiliation, 재조정이라고 한다.
Virtual DOM이 무엇인지,
컴포넌트 상태가 바뀌었을 때 리액트는 어떻게 해당 컴포넌트를 리-렌더링시키는지,
어떻게 변경된 부분만 DOM에 마운트 되는지,
hook은 컴포넌트와 어떤 방식으로 매핑되어 사용되는지,
이벤트 구현은 어떻게 되어 있는지
react
renderer
reconciler
scheduler
event
컴포넌트가 "리렌더링"된다
=> 컴포넌트가 호출되고 그 결과가 VDOM에 반영된다는 것이지 DOM에 마운트되어 페인트 된다는 뜻은 아니다.