렌더링
리액트에서 렌더링이란 어떤 것을 의미할까요? 렌더링이란 다양한 레벨에서 사용되는 추상적인 개념이지만 이미지를 그려내는 과정, 즉 리액트에서 렌더링이란 화면에 특정한 요소를 그려내는 것을 의미합니다. 리액트는 개발자가 화면이 어떻게 보일지에 집중할 수 있게 간결한 API를 제공하고 컴포넌트라는 작은 단위로 개발할 수 있게 해주는데요, 가상 DOM을 유지해 렌더 간 효율적으로 DOM을 업데이트해 부드럽고 빠른 사용자 경험을 제공합니다.
1.1 React, https://reactjs.org/
렌더링은 사용자가 처음 페이지에 방문했을 때 일어나는데요, 리액트에서 렌더링은 UI와 결합된 상태가 업데이트될 때 발생하도록 하는 모델을 갖고 있습니다. 상태라는 것은 개발자 입장에서 관리해야 하는 데이터들, 즉 언제든지 사용자의 액션에 의해 변경될 여지가 있는 저장된 데이터를 의미합니다. 이 데이터는 상태 업데이트 함수를 통해서 변경되었을 때 리렌더가 발생합니다. 특정 컴포넌트의 상태의 업데이트는 기본적으로 해당 컴포넌트와 그 하위에 있는 자식 컴포넌트의 렌더링으로 이어집니다. 따라서 상태가 변하면 해당 컴포넌트를 포함한 모든 자식 컴포넌트들이 리렌더링 된다는 리액트의 설계를 이해하는 것이 렌더링에 있어 기본이 되는 사항입니다.
리액트 렌더링
브라우저는 DOM이 변경될 때마다 기본적으로 다음과 같은 과정을 거치게 되는데요,
- HTML을 파싱해서 DOM 생성
- CSS를 파싱해서 CSSOM 생성
- DOM, CSSOM 결합해서 render tree 생성(스타일과 태그를 결합)
- render tree와 viewport의 width를 통해서 각 요소들의 위치와 크기를 계산(Layout)
- 계산된 정보를 이용해 Render Tree 요소들을 픽셀로 그림(Paint)
DOM이 업데이트되고 화면에 변경된 사항들이 그려지는 과정을 Critical Rendering Path라고 부릅니다. Layout과 Paint과정은 브라우저가 각 요소들이 화면 어디에 출력될지를 매번 계산하고 그 값들을 이용해서 픽셀점 하나하나 찍어주는 작업이기 때문에 적지 않은 비용이 필요한 프로세스입니다. UI를 변경하기 위해서는 DOM 조작이 필요한데 매 변경마다 위 과정을 수행하게 되면 자원이 많이 들고 화면이 업데이트될 때까지 사용자가 기다리는 시간이 불필요하게 길어질 수 있습니다. 리액트는 이를 해결하고자 가상 DOM을 유지하고 렌더 간 재조정을 통해서 업데이트가 필요한 최소한의 변경 사항들만 실제 DOM에 반영합니다.
가상 DOM은 실제 DOM의 가벼운 복사본인데요, 상태 업데이트 등으로 리렌더가 발생하면 리액트는 화면에 이를 반영하기 위해 내부적으로 다음과 같은 과정을 거치게 됩니다.
- 기존 컴포넌트의 UI를 재사용할지 확인한다
- 변경이 필요하면, 컴포넌트 함수 호출로 새로운 가상 DOM 생성
- 2번의 결과를 통해서 새로운 가상 DOM을 생성
- 이전의 가상 DOM과 새로운 가상 DOM을 비교해서 변경된 부분만 DOM에 적용
1.2 React Virtual DOM Explained in Simple English, https://programmingwithmosh.com/react/react-virtual-dom-explained/
리액트는 UI 변화에 필요한 DOM 변경 사항들을 매번 즉각적으로 실제 DOM에 적용하는 것이 아니라 DOM과 유사한 가상 DOM을 만들어 냅니다. 우선 컴포넌트의 재사용 여부를 파악한 후 새로 만들어진 가상 DOM 트리를 이전의 것과 비교하는데요, 비교 과정을 통해서 두 가상 DOM 트리 간 최소한의 변경 사항만 실제 DOM에 반영하는 것이죠.
DOM을 직접 조작하면 매번 복잡한 렌더링 과정을 거쳐야 하기 때문에 실제로 변화가 필요한 DOM 요소들을 찾아낸 후 한 번에 해당 DOM 요소들을 조작합니다. 리액트는 이러한 메커니즘으로 개발자를 대신해 렌더링 과정을 효율적으로 수행하는데요, 결국 브라우저에서 수행되는 Critical Rendering Path의 빈도를 최소화하고 UI를 업데이트합니다.
리액트가 렌더 간 DOM 트리를 비교하는 과정과 규칙은 이 글에서 더 자세히 볼 수 있습니다.