속성값(props)이나 상탯값(state)이 변경되면 리액트가 자동으로 컴포넌트 함수를 이용해서 화면을 다시 그린다. 리렌더링은 다음과 같은 과정을 거친다.
1번의 단계에서는 props
나 state
의 이전 값과 이후 값을 비교하고 이후 단계를 생략할 수 있다. 클래스 컴포넌트에서는 shouldComponentUpdate
메서드가 이 역할을 하고, 함수 컴포넌트에서는 React.memo
를 이용해서 구현할 수 있다.
2번 단계에서는 1번 단계에서 렌더링이 필요하다고 판단되면 컴포넌트 함수를 호출한다. 컴포넌트 함수를 호출해서 새로운 가상 돔을 만들고 이전에 만들었던 가상 돔과 비교해서 변경점을 찾는다. 그리고나서 변경된 부분만 실제 돔에 반영한다.
평상시에는 성능 최적화를 고민하지말고 코딩한 후에, 성능 이슈가 생기면 그때 고민해서 적용하는게 좋다.
React.memo
함수로 감싼 컴포넌트는 속성값 비교 함수가 호출되서 참, 거짓을 반환한다. 참이면 렌더링을 멈추고, 거짓이면 리렌더링을 진행한다.
렌더링 성능이 중요한 상황에서는 컴포넌트를 React.memo
함수로 감싸서 컴포넌트 함수의 실행과 가상 돔의 계산을 생략할 수 있다.
React.memo
와 useMemo
훅의 차이점React.memo
는 Higher-Order Components(HOC)이다. 즉, 컴포넌트를 인자로 받아 새로운 컴포넌트로 return해주는 함수이다. 일반 컴포넌트는 props
를 UI에 활용하는 반면, HOC는 인자로 받은 컴포넌트로 새로운 컴포넌트로 만든다. React.memo
도 컴포넌트를 인자로 받아서 같은 props를 받을 때 같은 결과를 렌더링 하는지를 비교하여 불필요한 컴포넌트 렌더링을 방지한다. 다만, 비교 시 오직 props만 체크한다. 컴포넌트 자체를 재사용할 수 있게 해준다.
useMemo
는 메모이즈된 값을 return하는 훅이다. 인자로 함수와 의존성 배열을 받는다. useMemo
는 의존성 배열 인자 중에 하나라도 변경되면 값을 재 계산한다. 이를 통해 매 렌더링 마다 소요되는 불필요한 계산을 피할 수 있다. 값만 재사용할 수 있게 해준다.
const memoizedValue = useMemo(() => computeValue(a,b), [a,b])
메모이제이션 기법은 초기에는 적용하지 않는게 좋고 성능 이슈가 발생하면 그때 적용하는게 좋다. 메모이제이션이 너무 많이 되면 오히려 성능이 떨어지는 이슈가 있다.
둘다 props
가 변하지 않으면 인자로 넘긴 함수는 재 실행되지 않고 이전에 저장된 결과를 반환한다는 점이 동일하다.
함수의 값이 변하지 않도록 관리하기
useState
와 useReducer
의 상탯값 변경 함수는 변하지 않기 때문에 onChange와 같은 속성에 상탯값 변경함수를 입력하면 매번 함수를 새로 생성하지 않는다. onChange={setCount}
useCallback()
을 사용할 수 있다.객체의 값이 변하지 않도록 관리하기
useMemo
를 사용해서 필요한 경우에만 속성 값이 변경되도록 할 수 있다.// useMemo 적용 전
<Select options={FRUITS.filter(item => item.price <= maxPrice)} />
// 적용 후
const fruits = useMemo(() => FRUITS.filter(item => item.price <= maxPrice), [
maxPrice,
])
// ...
<Select options={fruits} />
요소의 타입 또는 속성을 변경하는 경우
요소를 추가하거나 삭제하는 경우
무조건 useMemo
, useCallback
, React.memo
를 사용하는 것은 좋지 않다. 성능을 최적화하는 코드는 가독성이 안 좋고 유지보수 비용을 증가시킨다. 성능 이슈가 발생한 부분만 최적화 하자.
출처