React.memo & Memoization

코몽·2023년 6월 4일
0

Trouble shooting (Academic)

목록 보기
11/12

React.memo

리액트는 state가 변할 경우 해당 컴포넌트와 하위의 컴포넌트들을 모두 리렌더링 합니다.
그런데 state가 변한 컴포넌트의 경우 당연히 UI의 변화가 있을 것이기에 리렌더링을 해야 하지만, 하위 컴포넌트의 경우에는 경우에는 props가 변화하지 않았다면 해당 컴포넌트가 UI가 변화하지 않았을 수도 있을 것입니다. 이런 경우에는 굳이 새롭게 컴포넌트 함수를 호출할 필요 없이 이전에 저장되어 있던 결과를 그대로 사용하는 것이 효율적입니다.

하지만 UI가 실질적으로 변화되었는지 안되었는지를 매번 리액트가 렌더링 과정에서 일일이 모든 컴포넌트 트리를 순회하면서 검사하는 것은 비효율적입니다. 따라서 리액트에서는 개발자에게 이 컴포넌트가 리렌더링이 되어야 할지 아닐지에 대한 여부를 표현할 수 있는 React.memo 함수를 제공하고 이를 통해 기존의 컴포넌트의 UI를 재사용할 지 판단하는 방법을 채택했습니다.

const MyComponent = React.memo(function MyComponent(props) {
  /* render using props */
});

React.memo로 감싸진 컴포넌트의 경우에는 상위 컴포넌트가 리렌더링 될 경우 무조건 리렌더링 되는 것이 아니라 컴포넌트의 이전의 Props와 다음 렌더링 때 사용될 Props를 비교해서 차이가 있을 경우에만 리렌더링을 수행합니다. 만약 차이가 없다면 리렌더링을 수행하지 않고 기존의 렌더링 결과를 재사용합니다. 이를 통해 컴포넌트에서 불필요하게 리렌더링이 되는 경우를 막을 수 있습니다.

이때 중요하게 생각해야 할 것은 props를 비교하는 방식입니다. React.memo는 기본적으로 props의 변화를 이전 props와 새로운 props를 shallow compare (===) 해서 판단합니다. 만약 이 기본적인 비교 로직을 사용하지 않고 비교를 판단하는 로직을 직접 작성하고 싶을 경우를 대비해서 React.memo는 변화를 판단하는 함수를 인자로 받을 수 있도록 설정해뒀습니다.

function MyComponent(props) {
  /* render using props */
}

function areEqual(prevProps, nextProps) {
  /*
  true를 return할 경우 이전 결과를 재사용
  false를 return할 경우 리렌더링을 수행
  props 객체는 리렌더링시 매번 변하기에 prevProps.name === nextProps.name
  과 같이 props 객체의 프로퍼티를 비교한다.
  */
}

export default React.memo(MyComponent, areEqual);

useMemo

useMemo는 리액트에서 을 memoization 할 수 있도록 해주는 함수입니다.

// useMemo(callbackFunction, deps]

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useMemo는 두가지 인자를 받습니다. 첫번째 인자는 콜백함수이며 이 함수에서 리턴하는 값이 메모됩니다. 두번째 인자는 의존성 배열입니다. 메모이제이션을 할 때 주의해야 할 점은 만약 새로운 값을 만들어서 사용해야 하는 상황임에도 불구하고 이전의 결과를 그대로 활용해버리면 버그가 발생할 수 있다는 점입니다.

위의 예시에서 a, b 라는 두가지 변수를 이용해서 메모이제이션 하기 위한 값을 계산하고 있습니다. 그런데 만약 a, b 라는 값이 변경되었는데 이전의 값을 그대로 활용해버리면 의도한 결과와 다른 결과가 나오게 될 것입니다.

이런 상황을 방지하기 위해서 useMemo에서는 의존성 배열을 인자로 받아, 의존성 배열에 있는 값 중 하나라도 이전 렌더링과 비교했을 때 변경되었다면 메모된 값을 활용하는 것이 아니라 새로운 값을 다시 계산합니다.

useCallback

useCallback은 useMemo를 조금 더 편리하게 사용할 수 있도록 만든 버전입니다.

일반적인 값들은 useMemo를 통해서 메모하기 편리합니다. 하지만 함수의 경우에는 useMemo를 사용해서 메모하게 되면 콜백함수에서 또다른 함수를 리턴하는 형태가 되게 됩니다. 이는 동작상에는 아무런 이상이 없지만 코드 스타일에 따라 문법적으로 다소 보기가 불편해지는 단점이 있습니다. 따라서 이러한 동작을 간소화한 useCallback이란 함수를 만들어서 제공해주고 있습니다.

Memoization 사용 순간

  1. 새로운 값을 만드는 연산이 복잡하다.
  2. 함수 컴포넌트의 이전 호출과, 다음 호출 간 사용하는 값의 동일성을 보장하고 싶다.

1번의 경우에는 만약 10000개의 요소를 가진 배열이 있다고 생각하면 이 배열을 매번 생성하는 것 보다는 메모해서 활용하는 것이 효율적일 것입니다.

2번의 경우에는 함수 컴포넌트의 호출 간 값들의 동일성을 보장하기 위해서입니다. 그렇다면 왜 동일성을 보장해야 할까요? 그 이유는 바로 React.memo 와 연동해서 사용하기 위해서입니다.

React.memo에서 props로 함수나 객체를 매번 새로 생성해서 전달하면 리렌더링이 발생하기에 Memoization을 적용하여 전달한다.

profile
프론트엔드 웹 개발자(React) https://code-d-monkey.tistory.com/

0개의 댓글