개발자
류준열

메모이제이션이 필요한 순간

메모이제이션은 자주 쓰이는 값(or 함수)를 캐싱하여 리렌더링시 새로 만들지 않게 하는 것이다.
리액트에서는 useMemo, useCallback, memo 를 통해 구현할 수 있는데 여기서 굳이 설명하지는 않는다.

어쨌든 메모이제이션의 목적은 불필요한 리렌더링을 방지하는 것이다.

그래서 성능 최적화를 위해 자주 변하지 않는 부분은 메모이제이션을 하는 것인데, 때로는 성능최적화가 아니더라도 메모이제이션이 필요한 순간이 있다.

바로 리렌더링 되지 않아야 할 컴포넌트가 리렌더링 될 때 이다.

불필요한 리렌더링을 방지하는 가장 근본적인 방법

일단 가장 먼저 불필요한 리렌더링을 방지하는 가장 좋은 방법은 부모 컴포넌트에 state 선언하는 것을 지양하는 것이다.
반대 예시는 App.tsx에 state를 선언하는 것이다. 그러면 해당 state가 변경 될 때 마다 페이지가 전체 리렌더링된다.

그렇기 때문에 state를 필요로 하는 컴포넌트에만 선언해야 한다.

그럼에도 불구하고 리렌더링을 막을 수 없거나, 구조상 state 선언이 리렌더링을 유발할 수 밖에 없다면 메모이제이션을 해야 한다.

memo로 컴포넌트를 메모이제이션 하는 방법

React.memo 는 고차컴포넌트로 첫번째 인자에 컴포넌트를 받고, 두번째 인자에 ((prev,next))=>boolean 콜백함수를 받는다.

React.memo로 래핑된 컴포넌트는 이전 props와 다음 props가 같을 때 리렌더링 되지 않는다.

그런데 props가 객체일때는 항상 다른 참조를 가지기 때문에 prev.props === next.propsfalse 이다.
(이해가 안가면 const a = {name : 'june'}; const b ={ name : 'june'}; a===b;을 콘솔에 쳐보자.)

이때 다음과 같이 memo의 두번째 인자를 이용할 수 있다. 두번째 인자에서 true를 반환하면 리렌더링 하지 않는다.

const Component = ({id,name}:PropsType) => {
    ...
}
// 이전 id와 현재 id 가 같으면 리렌더링 하지 않는다.
export default memo(Component,(prev,next)=>prev.id===next.id)

실제 상황

타이핑 할 때 마다 이미지도 리렌더링 되는 상황

아래 gif를 보면 input에 타이핑할때마다 input 의 value state를 참조하는 모든 컴포넌트가 리렌더링 되면서 이미지가 깜빡이는 상황이다.

메모이제이션 안되서 깜빡이는 현상

근본적인 해결책은 이미지가 input의 value state를 참조하지 않도록 하는 것이지만, 구조상 힘들다면 간단하게 메모이제이션해서 해결 할 수 있다.

// before
export default ProfileUpload

// after : 이전 props와 현재 props를 비교하여 img가 바뀌지 않았다면 
// ProfileUpload 컴포넌트를 메모이제이션 한다.
 export default React.memo(ProfileUpload, (prev, next) => {
   return prev.imgUrl === next.imgUrl;
});

img 변화 유무를 따져서 컴포넌트를 메모이제이션 한 모습이다. 메모이제이션하여 깜빡이 해결

타이머가 매 초 리렌더링 되면서 주변 컴포넌트도 리렌더링 되는 상황

회사에서 동료분이 타이머 깜빡이 해결이 안된다고 하셔서, 메모이제이션을 통해 해결한 사례이다. 메모이제이션 before

타이머가 업데이트 될 때마다 타이머 state를 참조하는 모든 컴포넌트가 리렌더링 되는 상황이다. 아래와 같이 props 변화 유무에 따라 컴포넌트를 메모이제이션 하여 해결할 수 있었다.

const SurveyBoardContent = ({ survey }: SurveyBoardContentProps) => {
  return (
    <SurveyBoardContentWrapper>
      <CozSurveyTheme>
        <Survey model={survey} />
      </CozSurveyTheme>
    </SurveyBoardContentWrapper>
  );
};

export default React.memo(SurveyBoardContent);

메모이제이션 after

메모이제이션은 꼭 필요한가?

아니다. 메모이제이션의 연산 비용이 리렌더링 비용보다 크다면 메모이제이션을 하는 것이 손해다.

아래 두 상황에 메모이제이션을 하고 나머지는 굳이 필요하진 않은 것 같다.

  • 리소스가 큰 불필요한 리렌더링
  • 리렌더링이 되는것이 버그인 상황

그리고 항상 같은 것을 렌더링하는데 리렌더링되는 경우에는 그냥 강제로 리렌더링을 막을 수도 있다.

memo(Component,()=>true)

그리고 react 19에서는 메모이제이션을 리액트 컴파일러가 알아서 해준다고 하니 곧 쓸모없어질 고민이다.