프론트엔드 첫걸음

성능개선 - react.memo로 불필요한 재평가 방지하기 본문

개발 공부/React

성능개선 - react.memo로 불필요한 재평가 방지하기

차정 2022. 8. 4. 12:10

리액트는 사용자 인터페이스 구축을 위한 자바스크립트 라이브러리이다.

리액트는 사용자 UI를 효과적으로 구성하며, 이에 대한 업데이트 역시 컴포넌트를 통해서 한다.

리액트 DOM은 웹에 대한 인터페이스이다. 리액트 자체는 UI구성만 관여하고,
실제 HTML요소들을 화면에 표시헤주는 것은 리액트 DOM이 한다.

리액트는 props(부모로부터의 data), state(내부 data), context(전역 data)가 변경될 때 컴포넌트를 변경시킨다.
리액트는 리액트 DOM에 변경사항을 알려주고, 리액트 DOM이 화면을 새롭게 출력하도록 한다.

이 때 리액트가 가상돔을 사용해서 리액트 돔에 변경사항에 대한 정보를 전달한다.

리액트가 상태 업데이트 후 갱신 전 후 의 상태차이를 인식하고
리액트가 컴포넌트 트리를 통해 구성한 가상 스냅샷인 가상 DOM을 만들면,
리액트DOM이 가상DOM과 일치하도록 실제DOM을 조작한다.

리액트에 의해 컴포넌트 함수가 재실행된다고 해서 실제 DOM의 각 부분들이 재렌더링 되는 것이 아니다.
컴포넌트 state, props, context 변경
=> 컴포넌트 재평가
=> 컴포넌트 함수 다시 실행 

=> 실제 DOM은 컴포넌트의 이전상태와 현재상태가 다를때만 재 렌더링 된다.

return (
	<> 
	  <ChildComponent/>
	</>
)

 return 문 안의 jsx 요소들은 컴포넌트 함수를 호출한 것과 같다.
(return ChildComponent ~ 는 ChildComponent 함수 실행한 것과 같다)

그래서 부모컴포넌트가 변경되어 재실행되면 자식컴포넌트도 재실행되는것이다
컴포넌트들을 재평가 하고  변경된 경우에만 재렌더링 된다.

컴포넌트가 매번 재실행 되지 않도록 하고 싶다면 useMemo를 사용한다.

 

import React from 'react'
import <MyParagraph> from './MyParagraph'

const ChildComponent = (props) = {
  return <MyParagraph>{props.show ? 'this is new' : ''} </MyParagraph>
}

export default React.memo(ChildComponent);

 

react.memo는 컴포넌트에 들어가는 모든 props를 확인한 뒤, 이를 기존의 props 값과 비교하도록 리액트에 전달한다.
그리고 props의 값이 바뀐 경우에만 컴포넌트를 재실행 및 재평가 한다.
부모컴포넌트가 변경되었음에도 자식컴포넌트에 내려주는 props는 변하지 않았다면 컴포넌트의 실행을 건너뛴다.
재실행되지 않는 자식컴포넌트에 포함된 다른 컴포넌트들도 재실행되지 않는다.

그러나 memo 메소드는 App에 변경이 발생할 때마다 memo된 컴포넌트 props의 기존값과 새로운 값을  비교한다
그러려면 props 기존값을 저장하고, 새 값과 비교하는 작업은 개별적인 성능 비용을 요구하게 된다.
memo 성능비용이 memo 효과보다 적은 경우에 Memo를 써야한다.

컴모넌트 트리가 매우 큰 경우 컴포넌트 트리의 상위에 위치한 컴포넌트를 memo하는 것은 전체 컴포넌트 트리에 대한 쓸데없는 재렌더링을 막을 수 있어서 효과적이다.(컴포넌트 상위에서 그쪽으로 뻗어나가는 불필요한 재평가를 잘라내 버리는 경우)
그러나 컴포넌트 트리가 매우 작은 경우나 부모컴포넌트가 재평가될때마다 props값이 변화하는 경우에는 효과적이지 않다.
memo로 얻는 이득이 적거나 어차피 재평가되어 재렌더링 되기 때문이다.