React 렌더링 최적화

경용·2022년 11월 7일
0

렌더링

1) 렌더링이란?

렌더링이란 화면에 특정한 요소를 그려내는 것을 의미한다. 사실 이 렌더링 과정을 잘 처리해주는 것이 우리가 Vanila JavaScript를 사용하지 않고 React 같은 UI 라이브러리 또는 프레임워크를 사용하는 이유이다.
브라우저에서 렌더링이란 결국 DOM요소를 계산하고 그려내는 것을 의미한다. HTML과 CSS를 통해서 만들어지고 계산된 DOM과 CSSOM은 결합되고, 위치를 계산하고, 최종적으로 브라우저에 그려진다. 그리고 우리는 브라우저에서 제공하는 DOM API를 JavaScript를 통해 호출하면서 브라우저에 그려진 화면을 변화시킨다.

하지만 Vanila JavaScript를 이용해서 DOM에 직접 접근하고 수정하는 것(명령형), 그리고 이를 최적화 하는 것은 애플리케이션의 규모가 커지면 커질수록 관리하기 힘들어진다. 그래서 개발자들은 애플리케이션에서 보여주고 싶은 핵심 UI를 “선언”하기만 하면 실제로 DOM을 조작해서 UI를 그려내고 변화시키는 일은 라이브러리나 프레임워크가 대신 해주는 방식을 찾게 된다(선언적 개발).
이런 니즈에 맞춰서 React, Vue, Angular등의 라이브러리, 프레임워크가 등장하게 되고 그 중에서 React가 현재 가장 많이 사용되고 있다. 실제로 React 공식문서를 보면 가장 첫번째 장점으로 “선언형”을 내세우고 있다.

이처럼 React는 선언형으로 실제 렌더링 과정은 React에서 대신 처리해주고, 개발자는 UI를 설계하는대만 집중하게 해준다. 하지만 때로는 React 내부에서 처리해주는 렌더링을 최적화 해야 되는 상황이 발생한다. 이러한 상황에서는 React 내부에서 렌더링이 언제 발생하는지, 어떤 과정을 거쳐서 이루어지는지를 이해하고 있어야 각 과정에서 렌더링을 최적화 할 수 있게 된다.

2) 리렌더링이 되는 시점

리액트에서 리렌더링은 언제 발생할까? 좀 더 근본적으로 리액트에서 state를 왜 사용할까?

리액트에서 state를 사용하는 이유는 UI와 상태(state)를 연동시키기 위해서다. 근본적으로 UI는 어떠한 데이터가 있고 그것을 보기 편한 형태로 표현한 것이다. 리액트는 이를 이해하고 UI와 연동되어야 하고, 변할 여지가 있는 데이터들을 state라는 형태로 사용할 수 있게 해주었다. 그리고 데이터가 변경되었을 때 UI가 그에 맞춰서 변화하기 위해서 state를 변경시키는 방법을 제한시키고(setState), 이 함수가 호출 될 때 마다 리렌더링이 되도록 설계하였다.

이런 이유로 인해서 리액트에서 리렌더링이 발생하는 시점은 state가 변했을 때이다. 특정 컴포넌트의 state가 변한다면, 해당 컴포넌트와 해당 컴포넌트의 하위에 있는 모든 컴포넌트들은 리렌더링이 발생하게 된다.

3) 렌더링 과정

리액트는 state가 변화했을 때 리렌더링을 발생시킨다고 했는데, 이 과정을 좀 더 뜯어보면 state가 변화되고 최종적으로 브라우저상의 UI에 반영되기까지 각 컴포넌트에서는 크게 아래의 4단계를 거치게 된다.

  1. 기존 컴포넌트의 UI를 재사용할 지 확인한다.
  2. 함수 컴포넌트: 컴포넌트 함수를 호출한다 / Class 컴포넌트: render 메소드를 호출한다.
  3. 2의 결과를 통해서 새로운 VirtualDOM을 생성한다.
  4. 이전의 VirtualDOM과 새로운 VirtualDOM을 비교해서 실제 변경된 부분만 DOM에 적용한다.

VirtualDOM을 왜 사용하는걸까?
브라우저는 근본적으로 화면을 보여주기 위해서 HTML, CSS, JavaScript를 다운로드 받고 그를 처리해서 화면에 픽셀 형태로 그려낸다. 이 과정을 CRP(Critical Rendering Path)라고 부른다. Critical Rendering Path는 기본적으로 아래의 과정을 수행한다.

  1. HTML을 파싱해서 DOM을 만든다.
  2. CSS를 파싱해서 CSSOM을 만든다.
  3. DOM과 CSSOM을 결합해서 Render Tree를 만든다.
  4. Render Tree와 Viewport의 witdh를 통해서 각 요소들의 위치와 크기를 계산한다.(Layout)
  5. 지금까지 계산된 정보를 이용해 Render Tree상의 요소들을 실제 Pixel로 그려낸다. (Paint)

이후 DOM 또는 CSSOM이 수정될 때 마다 위의 과정을 반복한다. 따라서 이 과정을 최적화 하는 것이 퍼포먼스상에 중요한 포인트이다. 그런데 위 과정중에서 Layout, Paint 과정은 특히나 많은 계산을 필요로하는 부분이다. 따라서 리액트는 이 CRP이 수행되는 횟수를 최적화 하기 위해서 VirtualDOM을 사용한다.

UI를 변화하기 위해서는 많은 DOM 조작이 필요하다. 하나하나의 DOM조작마다 CRP가 수행될 것이고 이는 곧 브라우저에게 많은 연산을 요구하게 되고, 퍼포먼스를 저하시키는 요인이 될 수 있다. 리액트는 이를 해결하고자 VirtualDOM이란 개념을 도입했다.

리액트에서는 UI의 변화가 발생하면 변화에 필요한 DOM조작들을 매번 바로 실제 DOM에 적용하는 것이 아니라, VirtualDOM이란 리액트가 관리하고 있는 DOM과 유사한 객체형태로 만들어 낸다. 그리고 이전의 VirtualDOM과 새로운 VirtualDOM을 비교해서 실제로 변화가 필요한 DOM요소들을 찾아낸다. 그 다음에 한번에 해당 DOM요소들을 조작한다.

이 처리를 통해서 브라우저에서 수행되는 CRP의 빈도를 줄일 수 있고 이게 VIrtualDOM을 이용해서 리액트가 수행하는 최적화다. 즉, 4.이전의 VirtualDOM과 새로운 VirtualDOM을 비교해서 실제 변경된 부분만 DOM에 적용한다. 에 해당하는 최적화는 리액트 내부적으로 수행하고 있다는 의미이고 이부분은 리액트를 사용하는 개발자 입장에서는 따로 최적화를 수행할 부분은 아니다.
리액트를 사용하는 개발자가 할 수 있는 최적화는 1. 기존 컴포넌트의 UI를 재사용할 지 확인한다.3. 2의 결과를 통해서 새로운 VirtualDOM을 생성한다. 두 부분에 해당하는 최적화이다.

1. 기존 컴포넌트의 UI를 재사용할 지 확인한다. 의 경우에는 만약 리렌더링 될 컴포넌트의 UI가 이전의 UI와 동일하다고 판단되는 경우 새롭게 컴포넌트 함수를 호출하지 않고 이전의 결과값을 그대로 사용하도록 함으로서 최적화를 수행할 수 있다.
3. 2의 결과를 통해서 새로운 VirtualDOM을 생성한다. 의 경우는 컴포넌트 함수가 호출되면서 만들어질 VirtualDOM의 형태를 비교적 차이가 적은 형태로 만들어지도록 하는 것이다.

언제 최적화를 해야할까?

최적화를 하기 전 항상 명심해야 할 사항이 있다.

“최적화는 공짜가 아닙니다”

최적화를 하기 위해서는 최적화를 위한 코드가 프로젝트에 추가되어야 할 수 있고, 이는 프로젝트의 복잡도를 증가시킨다. 또 최적화를 하기 위한 개발자의 시간과 노력이 투입되게 되는데 이는 꽤나 비싼 비용을 투자해야 하는 작업이다.

현업에서의, 실제 필드에서의 개발자는 자기만족을 위해서 개발을 하는 사람이 아니다. 현업에 있는 개발자는 개발을 통해서 가치를 창출해내야 한다. 만약 개발자가 하는 개발이 아무런 가치도 창출해내지 못한다면 절대 좋은 평가를 받을 수 없을 것이다. 자신의 도전정신, 탐구욕을 채우기 위한 개발은 사이드 프로젝트 등을 통해서 수행해야 한다.

따라서 최적화를 해야 하는 시기는 이 최적화가 명확히 가치를 창출해낼 수 있을 것이라고 기대되는 상황
즉, 현재의 프로젝트에 성능적인 이슈가 발생했거나, 발생할 가능성이 있고 이를 해결해야 될 필요성이 있는 상황에서 수행하는 것이 바람직하다.

profile
문제를 객관적으로. 그 후 true / false

0개의 댓글