React로 개발을 하다보면 항상 맞닥뜨리는 문제가 하나있다. 바로 rendering에 관한것이다.
특히 불필요한 rendering을 줄이는것은 react 개발자가 평생 가져가야할 과제일 것이다. 본격적으로 rendering 최적화에 대해 이야기하기 전에 rendering에 대해 간단히 알아보도록 하자.
Rendering in REACT
React에서 말하는 rendering은 props와 state의 상태를 기반으로 component를 구성하는 작업을 말한다. Rendering이 이루어지는 과정은 다음과 같다.
Mount가 이루어지는 과정
function component호출- 구현부분 실행
Props확인,hook실행,variables및function생성
hook에 등록해둔 상태값,effect등은 별도 메모리에 저장되어 관리 return실행 (rendering시작)render실행 (가장DOM생성)commit단계 (실제DOM에 반영)useLayoutEffect
브라우저가paint하기전에useLayoutEffect에 등록해둔effect가 동기로 실행
이때state,store등의 변경이 있다면re-rendering된다PaintuseEffect
mount되어 화면이 그려진 직후,useEffect에 등록해둔effect가 비동기로 실행
Rerendering in REACT
function component재호출- 구현부 실행
return실행render실행
새로운 가상DOM생성 후 이전과 비교하여 달라진 부분을 탐색하고, 실제 변경을 반영할 부분을 결정한다commit단계에서 달라진 부분만 실제DOM에 반영한다useLayoutEffect
useLayoutEffect에 등록된effect가 동기로 실행
이때,state,redux-store등의 변경이 있다면 한번 더 re-rendering 된다PaintuseEffect
update되어 화면이 그려진 직후,useEffect에 등록해둔effect가 비동기로 실행
useEffect & useLayoutEffect
useEffect에 등록된 effect는 화면이 그려진 직후 비동기로 실행된다
useLayoutEffect에 등록된 effect는 DOM 반영 후 화면이 그려지기 직전 동기로 실행된다.
rerendering이 일어나는 조건
state가 변경 되었을 때
react에서 상태관리를 위해 사용하는state가setState라는 메소드를 이용해 값이 변경되었을때 이를 감지하고rerendering한다- 전달받은
props가 변경 되었을 때
부모component로부터 받은props가 변경되었을때rerendering을 한다 - 부모
component가rendering될 때
Component rendering 최적화 기법
React에서는 이러한 낭비를 막기위해 rendering을 최적화할 수 있는 몇가지 주요한 API를 제공해주고 있다.
React.memo()
HOC(High-order component)형태로 내장된 메소드이다. props가 변경되었는지 확인하고, 변경되지 않았을 경우 rerendering 되지 않도록 한다. 함수형, class형 component 둘다 wrapping해서 사용할 수 있다.
2. React.Component.shouldComponentUpdate
rendering 초기에 호출되는 선택적 class component life cycle 메소드 이다. false를 return하면 rendering을 하지 않는다. 보통 이전 props 및 상태가 달라졌는지 확인하고, 변화가 없다면 false를 return한다.
3. React.PureComponent
위에서 언급한 props 및 상태 비교는 shoudComponentUpdate를 구현하는 가장 일반적인 방법이기 때문에, PureComponent는 해당 로직을 기본적으로 구현해준다.
Props refer optimization
function component의 경우 react는 동일한 참조를 재사용 할 수 있도록 유용한 hook을 제공해준다. 객체를 만들거나 복잡한 연산을 하는 것과 같은 일반적인 데이터를 위한 useMemo와 callback 함수를 만드는데 사용할 수 있는 useCallback이다. 하지만 모든 함수나 객체에 대해 useMemo와 useCallback을 사용하게 될 경우 배보다 배꼽이 더 큰 성능이슈가 발생할 수 있다. 변경을 체크를 하는것 자체가 props를 비교하는 비용이 발생하기 때문이다. 관련 내용을 언급한 Dan abramov의 트윗
React Forget 의 등장예고
리액트 팀이 ReactConf 2021에서 React Forget이라는 실험적인 컴파일러를 시연했다. 이것은 자동으로 memoization 기능을 추가하도록 하는것. 흥미로운 점은 hook의 종속성 배열을 memoization할 뿐만 아니라 JSX요소 반환값도 memoization한다는 것이다. 다시 간단히 말해서 React Forget이 컴포넌트 트리 전체에서 불필요한 rendering을 효과적으로 제거할 수 있다는 것을 뜻한다.
현재 React Forget은 출시되지 않았지만, 작업이 잘 진행되고 있다는 힌트는 존재한다. useEvent가 close 되었고, 다른 곳에서 'auto memoization 으로 인해 rendering 문제가 사라지면 어떨까요?' 라고 언급하기도 했다.
결국, 개발자들이 다양한 툴과 동작의 trade-off를 명확하게 이해해서 자신의 상황에 가장 적합한것이 무엇인지 결정하고, 이에 기반해서 기술스택이나 서비스를 만들어나가는 것이 중요한것이 아닐까.
- https://github.com/donavon/hook-flow
- https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/